praisonaiagents 0.0.143__py3-none-any.whl → 0.0.145__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- praisonaiagents/__init__.py +3 -0
- praisonaiagents/agent/__init__.py +2 -1
- praisonaiagents/agent/agent.py +0 -17
- praisonaiagents/agent/context_agent.py +2315 -0
- praisonaiagents/agents/agents.py +30 -12
- praisonaiagents/knowledge/knowledge.py +9 -1
- praisonaiagents/memory/memory.py +39 -15
- praisonaiagents/task/task.py +7 -6
- {praisonaiagents-0.0.143.dist-info → praisonaiagents-0.0.145.dist-info}/METADATA +1 -1
- {praisonaiagents-0.0.143.dist-info → praisonaiagents-0.0.145.dist-info}/RECORD +12 -11
- {praisonaiagents-0.0.143.dist-info → praisonaiagents-0.0.145.dist-info}/WHEEL +0 -0
- {praisonaiagents-0.0.143.dist-info → praisonaiagents-0.0.145.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,2315 @@
|
|
1
|
+
"""
|
2
|
+
ContextAgent - Advanced Context Engineering for AI Coding Assistants
|
3
|
+
|
4
|
+
This class implements proper Context Engineering principles following the PRD template:
|
5
|
+
- 10x better than prompt engineering
|
6
|
+
- 100x better than vibe coding
|
7
|
+
- Comprehensive context generation for first-try implementation success
|
8
|
+
- Systematic codebase analysis with modern tools
|
9
|
+
- PRP (Product Requirements Prompt) generation
|
10
|
+
- Validation loops and quality gates
|
11
|
+
- SAVES EVERY AGENT RESPONSE ALONG THE WAY for complete traceability
|
12
|
+
"""
|
13
|
+
|
14
|
+
import os
|
15
|
+
import json
|
16
|
+
import asyncio
|
17
|
+
import glob
|
18
|
+
import subprocess
|
19
|
+
import ast
|
20
|
+
import logging
|
21
|
+
from datetime import datetime
|
22
|
+
from pathlib import Path
|
23
|
+
from typing import Optional, Any, Dict, Union, List
|
24
|
+
from ..agent.agent import Agent
|
25
|
+
from ..task import Task
|
26
|
+
|
27
|
+
class ContextAgent(Agent):
|
28
|
+
"""
|
29
|
+
Advanced Context Engineering Agent - Comprehensive context generation for AI coding assistants.
|
30
|
+
|
31
|
+
Implements the Context Engineering methodology from the PRD template:
|
32
|
+
|
33
|
+
Phase 1: Deep Codebase Analysis (using gitingest, AST analysis, etc.)
|
34
|
+
Phase 2: Pattern Extraction and Documentation
|
35
|
+
Phase 3: Comprehensive PRP Generation
|
36
|
+
Phase 4: Validation Framework Creation
|
37
|
+
Phase 5: Implementation Blueprint Generation
|
38
|
+
|
39
|
+
This follows the exact principles from the PRD template but adapted for PraisonAI architecture.
|
40
|
+
|
41
|
+
NEW: Saves every single agent response along the way for complete traceability!
|
42
|
+
"""
|
43
|
+
|
44
|
+
def __init__(
|
45
|
+
self,
|
46
|
+
name: Optional[str] = None,
|
47
|
+
role: Optional[str] = None,
|
48
|
+
goal: Optional[str] = None,
|
49
|
+
backstory: Optional[str] = None,
|
50
|
+
instructions: Optional[str] = None,
|
51
|
+
llm: Optional[Union[str, Any]] = None,
|
52
|
+
tools: Optional[List[Any]] = None,
|
53
|
+
project_path: Optional[str] = None,
|
54
|
+
auto_analyze: bool = True,
|
55
|
+
**kwargs
|
56
|
+
):
|
57
|
+
# Context Engineering specific defaults following PRD template
|
58
|
+
default_name = name or "Context Engineering Specialist"
|
59
|
+
default_role = role or "Expert Context Engineer and Product Requirements Analyst"
|
60
|
+
default_goal = goal or "Perform comprehensive codebase analysis and generate detailed PRPs for feature implementation"
|
61
|
+
default_backstory = backstory or """You are a world-class Context Engineering specialist with deep expertise in:
|
62
|
+
- Product Requirements Document (PRD) methodology
|
63
|
+
- Comprehensive codebase analysis and reverse engineering
|
64
|
+
- Feature implementation planning and architecture design
|
65
|
+
- Git repository ingestion and structured analysis
|
66
|
+
- Multi-agent orchestration for complex analysis tasks
|
67
|
+
|
68
|
+
Your approach follows systematic context engineering principles to understand codebases
|
69
|
+
deeply and generate actionable implementation guidance."""
|
70
|
+
|
71
|
+
# Initialize parent Agent
|
72
|
+
super().__init__(
|
73
|
+
name=default_name,
|
74
|
+
role=default_role,
|
75
|
+
goal=default_goal,
|
76
|
+
backstory=default_backstory,
|
77
|
+
instructions=instructions,
|
78
|
+
llm=llm,
|
79
|
+
tools=tools or self._get_context_engineering_tools(),
|
80
|
+
**kwargs
|
81
|
+
)
|
82
|
+
|
83
|
+
# Context Engineering specific attributes
|
84
|
+
self.project_path = project_path
|
85
|
+
self.auto_analyze = auto_analyze
|
86
|
+
self.analysis_results = {}
|
87
|
+
self.prp_results = {}
|
88
|
+
self.context_documentation = {}
|
89
|
+
self.implementation_blueprint = {}
|
90
|
+
|
91
|
+
# Enhanced logging and output management
|
92
|
+
self.debug_mode = os.getenv('LOGLEVEL', '').lower() == 'debug'
|
93
|
+
self.output_dir = Path(".praison/prp") # Save in .praison/prp folder
|
94
|
+
self.setup_output_directories() # Create directories first
|
95
|
+
self.setup_logging() # Then setup logging
|
96
|
+
|
97
|
+
# Agent interaction tracking for comprehensive output
|
98
|
+
self.agent_interactions = []
|
99
|
+
self.interaction_counter = 0
|
100
|
+
|
101
|
+
# Auto-analyze if requested and project_path provided
|
102
|
+
if self.auto_analyze and self.project_path:
|
103
|
+
self._perform_context_engineering_analysis()
|
104
|
+
|
105
|
+
def setup_logging(self):
|
106
|
+
"""Setup comprehensive logging based on debug mode."""
|
107
|
+
try:
|
108
|
+
# Create logger
|
109
|
+
self.logger = logging.getLogger(f"ContextAgent_{id(self)}")
|
110
|
+
self.logger.setLevel(logging.DEBUG if self.debug_mode else logging.INFO)
|
111
|
+
|
112
|
+
# Clear existing handlers
|
113
|
+
self.logger.handlers.clear()
|
114
|
+
|
115
|
+
# Console handler
|
116
|
+
console_handler = logging.StreamHandler()
|
117
|
+
console_handler.setLevel(logging.DEBUG if self.debug_mode else logging.INFO)
|
118
|
+
|
119
|
+
# File handler for debug mode
|
120
|
+
if self.debug_mode:
|
121
|
+
try:
|
122
|
+
log_file = self.output_dir / "debug_logs" / f"context_agent_debug_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
123
|
+
# Ensure the debug_logs directory exists
|
124
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
125
|
+
|
126
|
+
file_handler = logging.FileHandler(log_file)
|
127
|
+
file_handler.setLevel(logging.DEBUG)
|
128
|
+
|
129
|
+
# Detailed formatter for debug
|
130
|
+
debug_formatter = logging.Formatter(
|
131
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
|
132
|
+
)
|
133
|
+
file_handler.setFormatter(debug_formatter)
|
134
|
+
self.logger.addHandler(file_handler)
|
135
|
+
|
136
|
+
print(f"🐛 Debug logging enabled: {log_file}")
|
137
|
+
except Exception as e:
|
138
|
+
print(f"⚠️ Warning: Could not setup debug file logging: {e}")
|
139
|
+
# Continue without file logging if it fails
|
140
|
+
|
141
|
+
# Console formatter
|
142
|
+
console_formatter = logging.Formatter('%(levelname)s - %(message)s')
|
143
|
+
console_handler.setFormatter(console_formatter)
|
144
|
+
self.logger.addHandler(console_handler)
|
145
|
+
|
146
|
+
except Exception as e:
|
147
|
+
print(f"⚠️ Warning: Could not setup logging: {e}")
|
148
|
+
# Create a minimal logger as fallback
|
149
|
+
self.logger = logging.getLogger(f"ContextAgent_{id(self)}")
|
150
|
+
self.logger.setLevel(logging.INFO)
|
151
|
+
|
152
|
+
def setup_output_directories(self):
|
153
|
+
"""Setup all output directories for comprehensive saving."""
|
154
|
+
directories = [
|
155
|
+
self.output_dir,
|
156
|
+
self.output_dir / "agent_responses",
|
157
|
+
self.output_dir / "markdown_outputs",
|
158
|
+
self.output_dir / "debug_logs",
|
159
|
+
self.output_dir / "final_results"
|
160
|
+
]
|
161
|
+
|
162
|
+
for directory in directories:
|
163
|
+
directory.mkdir(parents=True, exist_ok=True)
|
164
|
+
|
165
|
+
if self.debug_mode:
|
166
|
+
print(f"🐛 Debug: Output directories created: {[str(d) for d in directories]}")
|
167
|
+
|
168
|
+
def log_debug(self, message: str, **kwargs):
|
169
|
+
"""Enhanced debug logging with optional data."""
|
170
|
+
if self.debug_mode and hasattr(self, 'logger') and self.logger:
|
171
|
+
self.logger.debug(f"{message} {kwargs if kwargs else ''}")
|
172
|
+
elif self.debug_mode:
|
173
|
+
# Fallback to print if logger not ready
|
174
|
+
print(f"🐛 DEBUG: {message} {kwargs if kwargs else ''}")
|
175
|
+
|
176
|
+
def save_markdown_output(self, content: str, filename: str, section_title: str = "Output"):
|
177
|
+
"""Save content as markdown file with proper formatting."""
|
178
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
179
|
+
|
180
|
+
markdown_content = f"""# {section_title}
|
181
|
+
|
182
|
+
**Generated:** {timestamp}
|
183
|
+
**ContextAgent Session:** {id(self)}
|
184
|
+
|
185
|
+
---
|
186
|
+
|
187
|
+
{content}
|
188
|
+
|
189
|
+
---
|
190
|
+
*Generated by ContextAgent - Context Engineering Specialist*
|
191
|
+
"""
|
192
|
+
|
193
|
+
# Save to markdown outputs directory
|
194
|
+
md_file = self.output_dir / "markdown_outputs" / f"{filename}.md"
|
195
|
+
with open(md_file, "w", encoding="utf-8") as f:
|
196
|
+
f.write(markdown_content)
|
197
|
+
|
198
|
+
print(f"📝 Markdown saved: {md_file}")
|
199
|
+
self.log_debug(f"Markdown output saved", file=str(md_file), length=len(content))
|
200
|
+
|
201
|
+
return str(md_file)
|
202
|
+
|
203
|
+
def save_comprehensive_session_report(self):
|
204
|
+
"""Save a comprehensive markdown report of the entire session (debug mode only)."""
|
205
|
+
if not self.debug_mode:
|
206
|
+
self.log_debug("Skipping comprehensive session report - not in debug mode")
|
207
|
+
return
|
208
|
+
|
209
|
+
if not self.agent_interactions:
|
210
|
+
self.log_debug("No agent interactions to save in comprehensive report")
|
211
|
+
return
|
212
|
+
|
213
|
+
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
214
|
+
|
215
|
+
# Generate comprehensive markdown report
|
216
|
+
report = f"""# ContextAgent Session Report
|
217
|
+
|
218
|
+
**Session ID:** {id(self)}
|
219
|
+
**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
220
|
+
**Total Agent Interactions:** {len(self.agent_interactions)}
|
221
|
+
**Debug Mode:** {'✅ Enabled' if self.debug_mode else '❌ Disabled'}
|
222
|
+
|
223
|
+
---
|
224
|
+
|
225
|
+
## 📋 Session Summary
|
226
|
+
|
227
|
+
This report contains all agent interactions and outputs from a complete ContextAgent session.
|
228
|
+
|
229
|
+
### 🎯 Agent Interactions Overview
|
230
|
+
|
231
|
+
"""
|
232
|
+
|
233
|
+
# Add interaction summary table
|
234
|
+
report += "| # | Agent | Phase | Response Length | Timestamp |\n"
|
235
|
+
report += "|---|-------|-------|-----------------|----------|\n"
|
236
|
+
|
237
|
+
for interaction in self.agent_interactions:
|
238
|
+
report += f"| {interaction['interaction_id']} | {interaction['agent_name']} | {interaction['phase']} | {len(interaction['response'])} chars | {interaction['timestamp']} |\n"
|
239
|
+
|
240
|
+
report += "\n---\n\n"
|
241
|
+
|
242
|
+
# Add detailed interactions
|
243
|
+
for i, interaction in enumerate(self.agent_interactions, 1):
|
244
|
+
report += f"""## 🤖 Agent {i}: {interaction['agent_name']}
|
245
|
+
|
246
|
+
**Phase:** {interaction['phase']}
|
247
|
+
**Timestamp:** {interaction['timestamp']}
|
248
|
+
**Response Length:** {len(interaction['response'])} characters
|
249
|
+
|
250
|
+
### 📝 Prompt
|
251
|
+
```
|
252
|
+
{interaction['prompt'][:1000]}{'... [truncated]' if len(interaction['prompt']) > 1000 else ''}
|
253
|
+
```
|
254
|
+
|
255
|
+
### 🎯 Response
|
256
|
+
{interaction['response']}
|
257
|
+
|
258
|
+
---
|
259
|
+
|
260
|
+
"""
|
261
|
+
|
262
|
+
# Save comprehensive report
|
263
|
+
report_file = self.save_markdown_output(
|
264
|
+
report,
|
265
|
+
f"comprehensive_session_report_{timestamp}",
|
266
|
+
"ContextAgent Comprehensive Session Report"
|
267
|
+
)
|
268
|
+
|
269
|
+
# Also save to final results
|
270
|
+
final_report_file = self.output_dir / "final_results" / f"session_report_{timestamp}.md"
|
271
|
+
with open(final_report_file, "w", encoding="utf-8") as f:
|
272
|
+
f.write(report)
|
273
|
+
|
274
|
+
print(f"📊 Comprehensive session report saved: {final_report_file}")
|
275
|
+
self.log_debug("Comprehensive session report generated", file=str(final_report_file))
|
276
|
+
|
277
|
+
return str(final_report_file)
|
278
|
+
|
279
|
+
def _save_agent_response(self, agent_name: str, prompt: str, response: str, phase: str = "", metadata: Dict[str, Any] = None) -> str:
|
280
|
+
"""Save individual agent response with mode-aware saving strategy."""
|
281
|
+
self.interaction_counter += 1
|
282
|
+
timestamp = datetime.now().isoformat()
|
283
|
+
|
284
|
+
# Enhanced debug logging
|
285
|
+
self.log_debug(f"Saving agent response",
|
286
|
+
agent=agent_name,
|
287
|
+
phase=phase,
|
288
|
+
response_length=len(response),
|
289
|
+
prompt_length=len(prompt))
|
290
|
+
|
291
|
+
interaction_data = {
|
292
|
+
"interaction_id": self.interaction_counter,
|
293
|
+
"timestamp": timestamp,
|
294
|
+
"phase": phase,
|
295
|
+
"agent_name": agent_name,
|
296
|
+
"prompt": prompt if self.debug_mode else prompt[:2000], # Full prompt in debug mode
|
297
|
+
"response": response,
|
298
|
+
"metadata": metadata or {}
|
299
|
+
}
|
300
|
+
|
301
|
+
# ALWAYS add to interactions list for final PRP
|
302
|
+
self.agent_interactions.append(interaction_data)
|
303
|
+
|
304
|
+
# ONLY save individual files in DEBUG MODE
|
305
|
+
if self.debug_mode:
|
306
|
+
# Save individual response file (JSON) - debug only
|
307
|
+
safe_agent_name = agent_name.lower().replace(' ', '_').replace('-', '_')
|
308
|
+
response_filename = f"{self.interaction_counter:03d}_{safe_agent_name}_{timestamp.replace(':', '_')}.json"
|
309
|
+
response_path = os.path.join(self.output_dir, "agent_responses", response_filename)
|
310
|
+
|
311
|
+
with open(response_path, "w") as f:
|
312
|
+
json.dump(interaction_data, f, indent=2)
|
313
|
+
|
314
|
+
# Save individual markdown files - debug only
|
315
|
+
md_filename = f"{self.interaction_counter:03d}_{safe_agent_name}_{timestamp.replace(':', '_')}"
|
316
|
+
self.save_markdown_output(
|
317
|
+
content=f"""## Agent: {agent_name}
|
318
|
+
**Phase:** {phase}
|
319
|
+
**Timestamp:** {timestamp}
|
320
|
+
**Response Length:** {len(response)} characters
|
321
|
+
|
322
|
+
### Prompt
|
323
|
+
```
|
324
|
+
{prompt[:1000]}{'... [truncated]' if len(prompt) > 1000 else ''}
|
325
|
+
```
|
326
|
+
|
327
|
+
### Response
|
328
|
+
{response}
|
329
|
+
""",
|
330
|
+
filename=md_filename,
|
331
|
+
section_title=f"{agent_name} - {phase}"
|
332
|
+
)
|
333
|
+
|
334
|
+
print(f"💾 Agent response saved: {response_filename}")
|
335
|
+
self.log_debug(f"Agent response and markdown saved", json_file=response_filename, interaction_id=self.interaction_counter)
|
336
|
+
return response_path
|
337
|
+
else:
|
338
|
+
# In non-debug mode, just collect for final PRP
|
339
|
+
print(f"📝 Agent response collected for final PRP: {agent_name}")
|
340
|
+
return "collected_for_final_prp"
|
341
|
+
|
342
|
+
def _chat_with_agent_and_save(self, agent: Agent, prompt: str, phase: str = "") -> str:
|
343
|
+
"""Chat with agent and automatically save the response."""
|
344
|
+
print(f" 💬 Chatting with {agent.name}...")
|
345
|
+
response = agent.chat(prompt)
|
346
|
+
|
347
|
+
# Save the interaction
|
348
|
+
self._save_agent_response(
|
349
|
+
agent_name=agent.name,
|
350
|
+
prompt=prompt,
|
351
|
+
response=response,
|
352
|
+
phase=phase,
|
353
|
+
metadata={
|
354
|
+
"agent_role": agent.role,
|
355
|
+
"agent_goal": agent.goal,
|
356
|
+
"response_length": len(response)
|
357
|
+
}
|
358
|
+
)
|
359
|
+
|
360
|
+
return response
|
361
|
+
|
362
|
+
def _get_context_engineering_tools(self) -> List[Any]:
|
363
|
+
"""Get Context Engineering specific tools following PRD methodology."""
|
364
|
+
return [
|
365
|
+
self.analyze_codebase_with_gitingest,
|
366
|
+
self.perform_ast_analysis,
|
367
|
+
self.extract_implementation_patterns,
|
368
|
+
self.analyze_test_patterns,
|
369
|
+
self.generate_comprehensive_prp,
|
370
|
+
self.create_validation_framework,
|
371
|
+
self.build_implementation_blueprint,
|
372
|
+
self.compile_context_documentation,
|
373
|
+
self.analyze_integration_points,
|
374
|
+
self.create_quality_gates
|
375
|
+
]
|
376
|
+
|
377
|
+
def _perform_context_engineering_analysis(self):
|
378
|
+
"""Perform comprehensive Context Engineering analysis following PRD template phases."""
|
379
|
+
try:
|
380
|
+
print("📊 PHASE 1: Deep Codebase Analysis with Modern Tools...")
|
381
|
+
self.codebase_analysis = self.analyze_codebase_with_gitingest(self.project_path)
|
382
|
+
|
383
|
+
print("🏗️ PHASE 2: AST Analysis and Pattern Extraction...")
|
384
|
+
ast_analysis = self.perform_ast_analysis(self.project_path)
|
385
|
+
self.pattern_library = self.extract_implementation_patterns(self.project_path, ast_analysis)
|
386
|
+
|
387
|
+
print("🔗 PHASE 3: Integration Point Analysis...")
|
388
|
+
self.integration_points = self.analyze_integration_points(self.project_path)
|
389
|
+
|
390
|
+
print("📚 PHASE 4: Context Documentation Compilation...")
|
391
|
+
self.context_documentation = self.compile_context_documentation(self.project_path)
|
392
|
+
|
393
|
+
print("✅ PHASE 5: Validation Framework Creation...")
|
394
|
+
self.validation_framework = self.create_validation_framework(self.project_path)
|
395
|
+
|
396
|
+
print("🎯 Context Engineering analysis complete following PRD methodology!")
|
397
|
+
self._save_context_engineering_results()
|
398
|
+
|
399
|
+
# Generate summary report following PRD template
|
400
|
+
self._generate_analysis_summary()
|
401
|
+
|
402
|
+
except Exception as e:
|
403
|
+
print(f"❌ Context Engineering analysis failed: {e}")
|
404
|
+
# Fallback to basic analysis if advanced tools fail
|
405
|
+
self._perform_fallback_analysis()
|
406
|
+
|
407
|
+
def analyze_codebase_with_gitingest(self, project_path: str) -> Dict[str, Any]:
|
408
|
+
"""Analyze codebase using gitingest for comprehensive understanding."""
|
409
|
+
print(" 🤖 Creating Gitingest-Powered Codebase Analyst...")
|
410
|
+
|
411
|
+
try:
|
412
|
+
# Try to use gitingest for comprehensive analysis
|
413
|
+
digest_content = self._run_gitingest_analysis(project_path)
|
414
|
+
|
415
|
+
if digest_content:
|
416
|
+
# Create specialized analyst to process gitingest output
|
417
|
+
gitingest_analyst = Agent(
|
418
|
+
name="Gitingest Codebase Analyst",
|
419
|
+
role="Expert Codebase Analysis Specialist using Gitingest",
|
420
|
+
goal="Perform comprehensive codebase analysis using gitingest output following PRD methodology",
|
421
|
+
instructions="""You are an expert at analyzing gitingest codebase digests following the PRD template methodology.
|
422
|
+
|
423
|
+
Analyze the gitingest output to extract:
|
424
|
+
1. PROJECT STRUCTURE: Complete directory organization and file hierarchies
|
425
|
+
2. CODE PATTERNS: All classes, functions, decorators, inheritance patterns
|
426
|
+
3. ARCHITECTURAL INSIGHTS: Design patterns, architectural styles, layer organization
|
427
|
+
4. NAMING CONVENTIONS: Consistent naming styles and patterns across the codebase
|
428
|
+
5. IMPORT PATTERNS: Module dependencies, relative vs absolute imports
|
429
|
+
6. TESTING PATTERNS: Test organization, frameworks, coverage approaches
|
430
|
+
7. DOCUMENTATION PATTERNS: Docstring styles, README structure, API documentation
|
431
|
+
8. CONFIGURATION PATTERNS: Environment handling, settings management
|
432
|
+
9. ERROR HANDLING: Exception patterns, logging approaches, error management
|
433
|
+
10. INTEGRATION PATTERNS: API integrations, database patterns, external services
|
434
|
+
|
435
|
+
Provide comprehensive analysis that follows the PRD template principles and enables
|
436
|
+
AI assistants to implement features that perfectly match existing codebase patterns.""",
|
437
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
438
|
+
verbose=getattr(self, 'verbose', True)
|
439
|
+
)
|
440
|
+
|
441
|
+
prompt = f"""Analyze this gitingest codebase digest following PRD template methodology:
|
442
|
+
|
443
|
+
PROJECT PATH: {project_path}
|
444
|
+
|
445
|
+
GITINGEST COMPREHENSIVE ANALYSIS:
|
446
|
+
{digest_content[:8000]} # Limit for context window
|
447
|
+
|
448
|
+
Perform deep analysis following the 10 categories above. Focus on patterns that would
|
449
|
+
enable an AI assistant to implement new features that perfectly match the existing
|
450
|
+
codebase style and architecture following PRD template principles."""
|
451
|
+
|
452
|
+
print(" 🔍 Gitingest Analyst analyzing comprehensive codebase digest...")
|
453
|
+
# Use the new save method
|
454
|
+
response = self._chat_with_agent_and_save(gitingest_analyst, prompt, "PHASE_1_GITINGEST_ANALYSIS")
|
455
|
+
|
456
|
+
analysis = {
|
457
|
+
"analysis_method": "gitingest",
|
458
|
+
"project_structure": {"analysis": response[:1000]},
|
459
|
+
"code_patterns": {"analysis": response},
|
460
|
+
"architectural_insights": {"analysis": response},
|
461
|
+
"gitingest_digest": digest_content[:2000],
|
462
|
+
"full_analysis": response
|
463
|
+
}
|
464
|
+
|
465
|
+
print(f" ✅ Gitingest comprehensive analysis complete")
|
466
|
+
return analysis
|
467
|
+
|
468
|
+
except Exception as e:
|
469
|
+
print(f" ⚠️ Gitingest analysis failed: {e}, falling back to manual analysis")
|
470
|
+
|
471
|
+
# Fallback to manual analysis if gitingest fails
|
472
|
+
return self._perform_manual_codebase_analysis(project_path)
|
473
|
+
|
474
|
+
def _run_gitingest_analysis(self, project_path: str) -> str:
|
475
|
+
"""Run gitingest analysis on the codebase."""
|
476
|
+
try:
|
477
|
+
# Try to run gitingest command
|
478
|
+
result = subprocess.run(
|
479
|
+
["gitingest", project_path, "--output", "-"],
|
480
|
+
capture_output=True,
|
481
|
+
text=True,
|
482
|
+
timeout=60
|
483
|
+
)
|
484
|
+
|
485
|
+
if result.returncode == 0:
|
486
|
+
return result.stdout
|
487
|
+
else:
|
488
|
+
print(f" ⚠️ Gitingest command failed: {result.stderr}")
|
489
|
+
return None
|
490
|
+
|
491
|
+
except subprocess.TimeoutExpired:
|
492
|
+
print(" ⚠️ Gitingest analysis timed out")
|
493
|
+
return None
|
494
|
+
except FileNotFoundError:
|
495
|
+
print(" ⚠️ Gitingest not found, trying alternative analysis")
|
496
|
+
return None
|
497
|
+
except Exception as e:
|
498
|
+
print(f" ⚠️ Gitingest error: {e}")
|
499
|
+
return None
|
500
|
+
|
501
|
+
def _perform_manual_codebase_analysis(self, project_path: str) -> Dict[str, Any]:
|
502
|
+
"""Fallback manual codebase analysis when gitingest is not available."""
|
503
|
+
print(" 🔧 Performing manual codebase analysis...")
|
504
|
+
|
505
|
+
# Get comprehensive file samples using manual methods
|
506
|
+
python_files = self._get_filtered_files(project_path, "*.py", 20)
|
507
|
+
config_files = self._get_filtered_files(project_path, "*.json", 5)
|
508
|
+
config_files.extend(self._get_filtered_files(project_path, "*.toml", 5))
|
509
|
+
config_files.extend(self._get_filtered_files(project_path, "*.yaml", 5))
|
510
|
+
doc_files = self._get_filtered_files(project_path, "*.md", 10)
|
511
|
+
|
512
|
+
# Create comprehensive manual analyst
|
513
|
+
manual_analyst = Agent(
|
514
|
+
name="Manual Codebase Analyst",
|
515
|
+
role="Expert Manual Codebase Analysis Specialist",
|
516
|
+
goal="Perform comprehensive manual codebase analysis following PRD methodology",
|
517
|
+
instructions="""Analyze the codebase samples following PRD template methodology for complete understanding.""",
|
518
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
519
|
+
verbose=getattr(self, 'verbose', True)
|
520
|
+
)
|
521
|
+
|
522
|
+
# Format comprehensive sample
|
523
|
+
comprehensive_content = self._format_comprehensive_samples(
|
524
|
+
python_files, config_files, doc_files, project_path
|
525
|
+
)
|
526
|
+
|
527
|
+
prompt = f"""Perform comprehensive manual codebase analysis following PRD template methodology:
|
528
|
+
|
529
|
+
PROJECT PATH: {project_path}
|
530
|
+
|
531
|
+
COMPREHENSIVE CODEBASE SAMPLES:
|
532
|
+
{comprehensive_content}
|
533
|
+
|
534
|
+
Analyze following PRD principles to extract patterns, conventions, and architectural insights."""
|
535
|
+
|
536
|
+
print(" 🔍 Manual Analyst analyzing codebase samples...")
|
537
|
+
# Use the new save method
|
538
|
+
response = self._chat_with_agent_and_save(manual_analyst, prompt, "PHASE_1_MANUAL_ANALYSIS")
|
539
|
+
|
540
|
+
analysis = {
|
541
|
+
"analysis_method": "manual",
|
542
|
+
"project_structure": {"analysis": response[:1000]},
|
543
|
+
"code_patterns": {"analysis": response},
|
544
|
+
"architectural_insights": {"analysis": response},
|
545
|
+
"full_analysis": response
|
546
|
+
}
|
547
|
+
|
548
|
+
print(f" ✅ Manual codebase analysis complete")
|
549
|
+
return analysis
|
550
|
+
|
551
|
+
def perform_ast_analysis(self, project_path: str) -> Dict[str, Any]:
|
552
|
+
"""Perform AST (Abstract Syntax Tree) analysis for code patterns."""
|
553
|
+
print(" 🤖 Creating AST Pattern Analyzer...")
|
554
|
+
|
555
|
+
ast_analyzer = Agent(
|
556
|
+
name="AST Pattern Analyzer",
|
557
|
+
role="Abstract Syntax Tree Analysis Expert",
|
558
|
+
goal="Extract code patterns and structures using AST analysis following PRD methodology",
|
559
|
+
instructions="""You are an expert at AST analysis for pattern extraction following PRD principles.
|
560
|
+
Analyze the AST information to identify:
|
561
|
+
1. Class hierarchies and inheritance patterns
|
562
|
+
2. Function signatures and patterns
|
563
|
+
3. Import dependencies and module structure
|
564
|
+
4. Decorator usage patterns
|
565
|
+
5. Exception handling patterns
|
566
|
+
6. Design pattern implementations
|
567
|
+
7. Code complexity metrics
|
568
|
+
8. API and interface patterns""",
|
569
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
570
|
+
verbose=getattr(self, 'verbose', True)
|
571
|
+
)
|
572
|
+
|
573
|
+
# Perform AST analysis on Python files
|
574
|
+
python_files = self._get_filtered_files(project_path, "*.py", 15)
|
575
|
+
ast_data = self._extract_ast_patterns(python_files)
|
576
|
+
|
577
|
+
prompt = f"""Analyze AST patterns from this codebase following PRD methodology:
|
578
|
+
|
579
|
+
PROJECT PATH: {project_path}
|
580
|
+
|
581
|
+
AST ANALYSIS DATA:
|
582
|
+
{json.dumps(ast_data, indent=2)[:4000]}
|
583
|
+
|
584
|
+
Extract comprehensive patterns that follow PRD template principles for implementation guidance."""
|
585
|
+
|
586
|
+
print(" 🔍 AST Analyzer processing code patterns...")
|
587
|
+
# Use the new save method
|
588
|
+
response = self._chat_with_agent_and_save(ast_analyzer, prompt, "PHASE_2_AST_ANALYSIS")
|
589
|
+
|
590
|
+
patterns = {
|
591
|
+
"ast_method": True,
|
592
|
+
"class_patterns": ast_data.get("classes", []),
|
593
|
+
"function_patterns": ast_data.get("functions", []),
|
594
|
+
"import_patterns": ast_data.get("imports", []),
|
595
|
+
"decorator_patterns": ast_data.get("decorators", []),
|
596
|
+
"full_analysis": response
|
597
|
+
}
|
598
|
+
|
599
|
+
print(f" ✅ AST analysis complete: {len(patterns.get('class_patterns', []))} classes, {len(patterns.get('function_patterns', []))} functions analyzed")
|
600
|
+
return patterns
|
601
|
+
|
602
|
+
def extract_implementation_patterns(self, project_path: str, ast_analysis: Dict[str, Any] = None) -> Dict[str, Any]:
|
603
|
+
"""Extract reusable implementation patterns following PRD methodology."""
|
604
|
+
print(" 🤖 Creating Implementation Pattern Extractor...")
|
605
|
+
|
606
|
+
pattern_extractor = Agent(
|
607
|
+
name="Implementation Pattern Extractor",
|
608
|
+
role="Code Pattern Recognition Expert following PRD methodology",
|
609
|
+
goal="Extract reusable implementation patterns and best practices following PRD template principles",
|
610
|
+
instructions="""You are an expert at identifying and extracting reusable implementation
|
611
|
+
patterns from codebases following PRD template methodology. Focus on patterns that enable
|
612
|
+
first-try implementation success. Analyze to find:
|
613
|
+
|
614
|
+
1. DESIGN PATTERNS: Factory, Observer, Strategy, Singleton, etc.
|
615
|
+
2. ARCHITECTURAL PATTERNS: MVC, Repository, Service Layer, etc.
|
616
|
+
3. CODE ORGANIZATION: Module structure, class hierarchies, function patterns
|
617
|
+
4. ERROR HANDLING: Try-catch patterns, custom exceptions, logging
|
618
|
+
5. DATA HANDLING: Validation patterns, serialization, database interactions
|
619
|
+
6. TESTING PATTERNS: Test structure, mocking approaches, fixture patterns
|
620
|
+
7. ASYNC PATTERNS: Async/await usage, concurrency patterns
|
621
|
+
8. INTEGRATION PATTERNS: API clients, external service patterns
|
622
|
+
9. CONFIGURATION PATTERNS: Settings management, environment variables
|
623
|
+
10. UTILITY PATTERNS: Common helper functions, decorators, context managers
|
624
|
+
|
625
|
+
For each pattern, provide the pattern name, where it's used, and how to replicate it
|
626
|
+
following PRD template principles.""",
|
627
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
628
|
+
verbose=getattr(self, 'verbose', True)
|
629
|
+
)
|
630
|
+
|
631
|
+
# Get representative code samples
|
632
|
+
samples = self._get_pattern_samples(project_path)
|
633
|
+
|
634
|
+
# Include AST analysis if available
|
635
|
+
ast_info = ""
|
636
|
+
if ast_analysis:
|
637
|
+
ast_info = f"\nAST ANALYSIS:\n{json.dumps(ast_analysis, indent=2)[:2000]}"
|
638
|
+
|
639
|
+
prompt = f"""Extract implementation patterns following PRD methodology:
|
640
|
+
|
641
|
+
PROJECT PATH: {project_path}
|
642
|
+
|
643
|
+
CODE SAMPLES:
|
644
|
+
{samples}
|
645
|
+
{ast_info}
|
646
|
+
|
647
|
+
Identify and document all reusable implementation patterns that follow PRD template
|
648
|
+
principles and would help an AI assistant implement new features using the same
|
649
|
+
patterns and best practices for first-try success."""
|
650
|
+
|
651
|
+
print(" 🔍 Pattern Extractor analyzing implementation patterns...")
|
652
|
+
# Use the new save method
|
653
|
+
response = self._chat_with_agent_and_save(pattern_extractor, prompt, "PHASE_2_PATTERN_EXTRACTION")
|
654
|
+
|
655
|
+
patterns = {
|
656
|
+
"patterns_identified": response.count("PATTERN"),
|
657
|
+
"design_patterns": [],
|
658
|
+
"architectural_patterns": [],
|
659
|
+
"testing_patterns": [],
|
660
|
+
"integration_patterns": [],
|
661
|
+
"full_analysis": response
|
662
|
+
}
|
663
|
+
|
664
|
+
print(f" ✅ Extracted implementation patterns following PRD methodology")
|
665
|
+
return patterns
|
666
|
+
|
667
|
+
def analyze_test_patterns(self, project_path: str) -> Dict[str, Any]:
|
668
|
+
"""Analyze testing patterns for validation framework creation."""
|
669
|
+
print(" 🤖 Creating Test Pattern Analyzer...")
|
670
|
+
|
671
|
+
test_analyzer = Agent(
|
672
|
+
name="Test Pattern Analyzer",
|
673
|
+
role="Testing Pattern Recognition Expert",
|
674
|
+
goal="Analyze testing patterns for comprehensive validation framework design",
|
675
|
+
instructions="""Analyze testing patterns to understand validation approaches and create
|
676
|
+
comprehensive test frameworks following PRD methodology.""",
|
677
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
678
|
+
verbose=getattr(self, 'verbose', True)
|
679
|
+
)
|
680
|
+
|
681
|
+
# Get test files
|
682
|
+
test_files = self._get_filtered_files(project_path, "*test*.py", 10)
|
683
|
+
test_files.extend(self._get_filtered_files(project_path, "test_*.py", 10))
|
684
|
+
test_files.extend(self._get_filtered_files(project_path, "conftest.py", 5))
|
685
|
+
|
686
|
+
test_content = self._format_sample_files(test_files, project_path)
|
687
|
+
|
688
|
+
prompt = f"""Analyze testing patterns following PRD methodology:
|
689
|
+
|
690
|
+
PROJECT PATH: {project_path}
|
691
|
+
|
692
|
+
TEST PATTERNS:
|
693
|
+
{test_content}
|
694
|
+
|
695
|
+
Extract testing patterns for validation framework creation following PRD principles."""
|
696
|
+
|
697
|
+
print(" 🔍 Test Analyzer processing testing patterns...")
|
698
|
+
# Use the new save method
|
699
|
+
response = self._chat_with_agent_and_save(test_analyzer, prompt, "PHASE_2_TEST_ANALYSIS")
|
700
|
+
|
701
|
+
patterns = {
|
702
|
+
"test_files_analyzed": len(test_files),
|
703
|
+
"testing_frameworks": [],
|
704
|
+
"test_patterns": [],
|
705
|
+
"validation_approaches": [],
|
706
|
+
"full_analysis": response
|
707
|
+
}
|
708
|
+
|
709
|
+
print(f" ✅ Test pattern analysis complete: {len(test_files)} test files analyzed")
|
710
|
+
return patterns
|
711
|
+
|
712
|
+
def generate_comprehensive_prp(self, feature_request: str, context_analysis: Dict[str, Any] = None) -> str:
|
713
|
+
"""Generate comprehensive Product Requirements Prompt following PRD template exactly."""
|
714
|
+
print(" 🤖 Creating PRP Generation Specialist following PRD template...")
|
715
|
+
|
716
|
+
if context_analysis is None:
|
717
|
+
context_analysis = {
|
718
|
+
"codebase_analysis": self.codebase_analysis,
|
719
|
+
"pattern_library": self.pattern_library,
|
720
|
+
"integration_points": getattr(self, 'integration_points', {}),
|
721
|
+
"validation_framework": self.validation_framework,
|
722
|
+
"context_documentation": self.context_documentation
|
723
|
+
}
|
724
|
+
|
725
|
+
prp_generator = Agent(
|
726
|
+
name="PRP Generation Specialist",
|
727
|
+
role="Product Requirements Prompt Expert following PRD Template",
|
728
|
+
goal="Generate comprehensive PRPs following the exact PRD template methodology for first-try implementation success",
|
729
|
+
instructions="""You are an expert at generating comprehensive Product Requirements Prompts (PRPs)
|
730
|
+
following the exact PRD template methodology. Your PRPs must follow the PRD template structure exactly:
|
731
|
+
|
732
|
+
## Purpose
|
733
|
+
Clear statement of what needs to be built
|
734
|
+
|
735
|
+
## Core Principles
|
736
|
+
1. Context is King: Include ALL necessary documentation, examples, and caveats
|
737
|
+
2. Validation Loops: Provide executable tests/lints the AI can run and fix
|
738
|
+
3. Information Dense: Use keywords and patterns from the codebase
|
739
|
+
4. Progressive Success: Start simple, validate, then enhance
|
740
|
+
|
741
|
+
## Goal
|
742
|
+
What needs to be built - specific about end state
|
743
|
+
|
744
|
+
## Why
|
745
|
+
- Business value and user impact
|
746
|
+
- Integration with existing features
|
747
|
+
- Problems this solves
|
748
|
+
|
749
|
+
## What
|
750
|
+
User-visible behavior and technical requirements
|
751
|
+
|
752
|
+
### Success Criteria
|
753
|
+
- [ ] Specific measurable outcomes
|
754
|
+
|
755
|
+
## All Needed Context
|
756
|
+
|
757
|
+
### Documentation & References
|
758
|
+
Complete list of all context needed
|
759
|
+
|
760
|
+
### Current Codebase tree
|
761
|
+
Run tree command output
|
762
|
+
|
763
|
+
### Desired Codebase tree
|
764
|
+
Files to be added with responsibilities
|
765
|
+
|
766
|
+
### Known Gotchas & Library Quirks
|
767
|
+
Critical implementation details
|
768
|
+
|
769
|
+
## Implementation Blueprint
|
770
|
+
|
771
|
+
### Data models and structure
|
772
|
+
Core data models for type safety
|
773
|
+
|
774
|
+
### List of tasks to be completed
|
775
|
+
Ordered task list for implementation
|
776
|
+
|
777
|
+
### Per task pseudocode
|
778
|
+
Detailed pseudocode with critical details
|
779
|
+
|
780
|
+
### Integration Points
|
781
|
+
Database, config, route changes needed
|
782
|
+
|
783
|
+
## Validation Loop
|
784
|
+
|
785
|
+
### Level 1: Syntax & Style
|
786
|
+
Executable commands for validation
|
787
|
+
|
788
|
+
### Level 2: Unit Tests
|
789
|
+
Test cases with expected outcomes
|
790
|
+
|
791
|
+
### Level 3: Integration Test
|
792
|
+
End-to-end validation steps
|
793
|
+
|
794
|
+
## Final Validation Checklist
|
795
|
+
Complete checklist for verification
|
796
|
+
|
797
|
+
## Anti-Patterns to Avoid
|
798
|
+
Common mistakes to prevent
|
799
|
+
|
800
|
+
## Confidence Score: X/10
|
801
|
+
Confidence level for one-pass implementation
|
802
|
+
|
803
|
+
Generate PRPs following this EXACT structure for first-try implementation success.""",
|
804
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
805
|
+
verbose=getattr(self, 'verbose', True)
|
806
|
+
)
|
807
|
+
|
808
|
+
prompt = f"""Generate a comprehensive Product Requirements Prompt (PRP) following the EXACT PRD template structure:
|
809
|
+
|
810
|
+
FEATURE REQUEST: {feature_request}
|
811
|
+
|
812
|
+
COMPREHENSIVE CONTEXT ANALYSIS:
|
813
|
+
{json.dumps(context_analysis, indent=2)[:6000]}
|
814
|
+
|
815
|
+
PROJECT PATH: {self.project_path}
|
816
|
+
|
817
|
+
Create a PRP that follows the PRD template methodology EXACTLY with all sections:
|
818
|
+
- Purpose and Core Principles
|
819
|
+
- Goal, Why, What with Success Criteria
|
820
|
+
- All Needed Context with Documentation & References
|
821
|
+
- Implementation Blueprint with tasks and pseudocode
|
822
|
+
- Validation Loop with executable commands
|
823
|
+
- Final checklist and confidence score
|
824
|
+
|
825
|
+
Include ALL necessary context for an AI assistant to implement this feature correctly
|
826
|
+
on the first try following PRD template principles."""
|
827
|
+
|
828
|
+
print(" 🔍 PRP Generator creating comprehensive PRP following PRD template...")
|
829
|
+
# Use the new save method
|
830
|
+
prp = self._chat_with_agent_and_save(prp_generator, prompt, "PHASE_3_PRP_GENERATION")
|
831
|
+
|
832
|
+
print(f" ✅ Comprehensive PRP generated following PRD template ({len(prp)} characters)")
|
833
|
+
return prp
|
834
|
+
|
835
|
+
# Continue with remaining methods using the new save approach...
|
836
|
+
def create_validation_framework(self, project_path: str) -> Dict[str, Any]:
|
837
|
+
"""Create comprehensive validation framework following PRD methodology."""
|
838
|
+
print(" 🤖 Creating Validation Framework Architect...")
|
839
|
+
|
840
|
+
validation_architect = Agent(
|
841
|
+
name="Validation Framework Architect",
|
842
|
+
role="Quality Assurance Expert following PRD methodology",
|
843
|
+
goal="Design comprehensive validation frameworks following PRD template principles",
|
844
|
+
instructions="""Design validation frameworks following PRD methodology that include:
|
845
|
+
1. SYNTAX VALIDATION: Linting, formatting, type checking
|
846
|
+
2. UNIT TESTING: Comprehensive test coverage strategies
|
847
|
+
3. INTEGRATION TESTING: End-to-end testing approaches
|
848
|
+
4. PERFORMANCE VALIDATION: Performance benchmarks
|
849
|
+
5. SECURITY VALIDATION: Security best practices
|
850
|
+
6. CODE QUALITY: Complexity analysis, maintainability
|
851
|
+
7. DOCUMENTATION VALIDATION: Documentation completeness
|
852
|
+
8. DEPENDENCY VALIDATION: Dependency analysis and security""",
|
853
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
854
|
+
verbose=getattr(self, 'verbose', True)
|
855
|
+
)
|
856
|
+
|
857
|
+
# Analyze existing validation patterns
|
858
|
+
test_files = self._get_filtered_files(project_path, "*test*.py", 10)
|
859
|
+
config_files = self._get_filtered_files(project_path, "*.toml", 3)
|
860
|
+
config_files.extend(self._get_filtered_files(project_path, "pyproject.toml", 1))
|
861
|
+
|
862
|
+
validation_content = self._format_sample_files(test_files + config_files, project_path)
|
863
|
+
|
864
|
+
prompt = f"""Design comprehensive validation framework following PRD methodology:
|
865
|
+
|
866
|
+
PROJECT PATH: {project_path}
|
867
|
+
|
868
|
+
EXISTING VALIDATION PATTERNS:
|
869
|
+
{validation_content}
|
870
|
+
|
871
|
+
Create validation framework with all 8 validation types and executable commands
|
872
|
+
following PRD template principles."""
|
873
|
+
|
874
|
+
print(" 🔍 Validation Architect designing framework...")
|
875
|
+
# Use the new save method
|
876
|
+
response = self._chat_with_agent_and_save(validation_architect, prompt, "PHASE_5_VALIDATION_FRAMEWORK")
|
877
|
+
|
878
|
+
framework = {
|
879
|
+
"syntax_validation": ["ruff check", "mypy"],
|
880
|
+
"testing_validation": ["pytest", "pytest --cov"],
|
881
|
+
"quality_gates": ["coverage report", "complexity analysis"],
|
882
|
+
"integration_tests": ["end-to-end test commands"],
|
883
|
+
"full_framework": response
|
884
|
+
}
|
885
|
+
|
886
|
+
print(f" ✅ Validation framework created following PRD methodology")
|
887
|
+
return framework
|
888
|
+
|
889
|
+
def compile_context_documentation(self, project_path: str) -> Dict[str, Any]:
|
890
|
+
"""Compile all context documentation following PRD methodology."""
|
891
|
+
print(" 🤖 Creating Context Documentation Compiler...")
|
892
|
+
|
893
|
+
doc_compiler = Agent(
|
894
|
+
name="Context Documentation Compiler",
|
895
|
+
role="Documentation Analysis Expert following PRD methodology",
|
896
|
+
goal="Compile comprehensive context documentation following PRD template principles",
|
897
|
+
instructions="""Compile all available documentation following PRD methodology including:
|
898
|
+
README files, API documentation, setup guides, architecture docs, and any other
|
899
|
+
relevant documentation that provides context for implementation.""",
|
900
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
901
|
+
verbose=getattr(self, 'verbose', True)
|
902
|
+
)
|
903
|
+
|
904
|
+
# Get documentation files
|
905
|
+
doc_files = self._get_filtered_files(project_path, "*.md", 10)
|
906
|
+
doc_files.extend(self._get_filtered_files(project_path, "*.rst", 5))
|
907
|
+
doc_files.extend(self._get_filtered_files(project_path, "*.txt", 5))
|
908
|
+
|
909
|
+
doc_content = self._format_sample_files(doc_files, project_path)
|
910
|
+
|
911
|
+
prompt = f"""Compile context documentation following PRD methodology:
|
912
|
+
|
913
|
+
PROJECT PATH: {project_path}
|
914
|
+
|
915
|
+
DOCUMENTATION FILES:
|
916
|
+
{doc_content}
|
917
|
+
|
918
|
+
Extract and organize all documentation that provides context for implementation
|
919
|
+
following PRD template principles."""
|
920
|
+
|
921
|
+
print(" 🔍 Documentation Compiler processing context docs...")
|
922
|
+
# Use the new save method
|
923
|
+
response = self._chat_with_agent_and_save(doc_compiler, prompt, "PHASE_4_DOCUMENTATION_COMPILATION")
|
924
|
+
|
925
|
+
docs = {
|
926
|
+
"readme_files": [f for f in doc_files if "readme" in f.lower()],
|
927
|
+
"api_docs": [f for f in doc_files if "api" in f.lower()],
|
928
|
+
"setup_docs": [f for f in doc_files if any(term in f.lower() for term in ["setup", "install", "config"])],
|
929
|
+
"full_documentation": response
|
930
|
+
}
|
931
|
+
|
932
|
+
print(f" ✅ Context documentation compiled: {len(doc_files)} files analyzed")
|
933
|
+
return docs
|
934
|
+
|
935
|
+
def analyze_integration_points(self, project_path: str) -> Dict[str, Any]:
|
936
|
+
"""Analyze integration points and external dependencies following PRD methodology."""
|
937
|
+
print(" 🤖 Creating Integration Point Analyzer...")
|
938
|
+
|
939
|
+
integration_analyzer = Agent(
|
940
|
+
name="Integration Point Analyzer",
|
941
|
+
role="Integration Analysis Expert following PRD methodology",
|
942
|
+
goal="Analyze all integration points and dependencies following PRD template principles",
|
943
|
+
instructions="""Analyze integration points following PRD methodology including:
|
944
|
+
APIs, databases, external services, configuration points, and any other
|
945
|
+
integration requirements that affect implementation.""",
|
946
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
947
|
+
verbose=getattr(self, 'verbose', True)
|
948
|
+
)
|
949
|
+
|
950
|
+
# Get configuration and dependency files
|
951
|
+
config_files = self._get_filtered_files(project_path, "*.toml", 5)
|
952
|
+
config_files.extend(self._get_filtered_files(project_path, "requirements*.txt", 5))
|
953
|
+
config_files.extend(self._get_filtered_files(project_path, "*.yaml", 5))
|
954
|
+
config_files.extend(self._get_filtered_files(project_path, "*.yml", 5))
|
955
|
+
config_files.extend(self._get_filtered_files(project_path, "*.json", 5))
|
956
|
+
|
957
|
+
integration_content = self._format_sample_files(config_files, project_path)
|
958
|
+
|
959
|
+
prompt = f"""Analyze integration points following PRD methodology:
|
960
|
+
|
961
|
+
PROJECT PATH: {project_path}
|
962
|
+
|
963
|
+
CONFIGURATION AND DEPENDENCY FILES:
|
964
|
+
{integration_content}
|
965
|
+
|
966
|
+
Identify all integration points, APIs, databases, and external dependencies
|
967
|
+
following PRD template principles."""
|
968
|
+
|
969
|
+
print(" 🔍 Integration Analyzer processing integration points...")
|
970
|
+
# Use the new save method
|
971
|
+
response = self._chat_with_agent_and_save(integration_analyzer, prompt, "PHASE_3_INTEGRATION_ANALYSIS")
|
972
|
+
|
973
|
+
integration_points = {
|
974
|
+
"apis": [],
|
975
|
+
"databases": [],
|
976
|
+
"external_services": [],
|
977
|
+
"configuration_points": [],
|
978
|
+
"dependencies": config_files,
|
979
|
+
"full_analysis": response
|
980
|
+
}
|
981
|
+
|
982
|
+
print(f" ✅ Integration point analysis complete: {len(config_files)} config files analyzed")
|
983
|
+
return integration_points
|
984
|
+
|
985
|
+
def build_implementation_blueprint(self, feature_request: str, context_analysis: Dict[str, Any] = None) -> Dict[str, Any]:
|
986
|
+
"""Build detailed implementation blueprint following PRD template."""
|
987
|
+
print(" 🤖 Creating Implementation Blueprint Architect...")
|
988
|
+
|
989
|
+
if context_analysis is None:
|
990
|
+
context_analysis = self.codebase_analysis
|
991
|
+
|
992
|
+
blueprint_architect = Agent(
|
993
|
+
name="Implementation Blueprint Architect",
|
994
|
+
role="Software Implementation Planning Expert following PRD methodology",
|
995
|
+
goal="Create detailed implementation blueprints following PRD template principles",
|
996
|
+
instructions="""Create implementation blueprints following PRD methodology that include:
|
997
|
+
1. TASK BREAKDOWN: Detailed task list in implementation order
|
998
|
+
2. FILE MODIFICATIONS: Specific files to modify and how
|
999
|
+
3. NEW FILE CREATION: New files needed and their purpose
|
1000
|
+
4. DEPENDENCY MANAGEMENT: Dependencies to add/update
|
1001
|
+
5. DATABASE CHANGES: Schema modifications if needed
|
1002
|
+
6. CONFIGURATION UPDATES: Config changes required
|
1003
|
+
7. TESTING REQUIREMENTS: Tests to create/update
|
1004
|
+
8. DOCUMENTATION UPDATES: Documentation to create/update
|
1005
|
+
9. INTEGRATION STEPS: How to integrate with existing systems
|
1006
|
+
10. VALIDATION CHECKPOINTS: Validation steps at each phase""",
|
1007
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
1008
|
+
verbose=getattr(self, 'verbose', True)
|
1009
|
+
)
|
1010
|
+
|
1011
|
+
prompt = f"""Create detailed implementation blueprint following PRD methodology:
|
1012
|
+
|
1013
|
+
FEATURE REQUEST: {feature_request}
|
1014
|
+
|
1015
|
+
CONTEXT ANALYSIS:
|
1016
|
+
{json.dumps(context_analysis, indent=2)[:3000]}
|
1017
|
+
|
1018
|
+
PROJECT PATH: {self.project_path}
|
1019
|
+
|
1020
|
+
Create comprehensive blueprint following PRD template with all 10 components for
|
1021
|
+
detailed, actionable implementation steps."""
|
1022
|
+
|
1023
|
+
print(" 🔍 Blueprint Architect creating detailed implementation plan...")
|
1024
|
+
# Use the new save method
|
1025
|
+
response = self._chat_with_agent_and_save(blueprint_architect, prompt, "PHASE_5_IMPLEMENTATION_BLUEPRINT")
|
1026
|
+
|
1027
|
+
blueprint = {
|
1028
|
+
"task_breakdown": [],
|
1029
|
+
"file_modifications": [],
|
1030
|
+
"new_files": [],
|
1031
|
+
"dependencies": [],
|
1032
|
+
"validation_checkpoints": [],
|
1033
|
+
"full_blueprint": response
|
1034
|
+
}
|
1035
|
+
|
1036
|
+
print(f" ✅ Implementation blueprint created following PRD methodology")
|
1037
|
+
return blueprint
|
1038
|
+
|
1039
|
+
# Helper methods and remaining functionality...
|
1040
|
+
def _get_filtered_files(self, project_path: str, pattern: str, max_files: int) -> List[str]:
|
1041
|
+
"""Get filtered files excluding unwanted directories following PRD principles."""
|
1042
|
+
try:
|
1043
|
+
files = glob.glob(os.path.join(project_path, "**", pattern), recursive=True)
|
1044
|
+
|
1045
|
+
# Filter out unwanted files following PRD methodology
|
1046
|
+
filtered = []
|
1047
|
+
exclude_patterns = [
|
1048
|
+
'__pycache__', '.pytest_cache', '.git', 'node_modules',
|
1049
|
+
'.venv', 'venv', 'dist', 'build', '.praison', '.chroma_db',
|
1050
|
+
'.ruff_cache', '.mypy_cache', 'context_engineering_output'
|
1051
|
+
]
|
1052
|
+
|
1053
|
+
for file_path in files:
|
1054
|
+
if not any(exclude in file_path for exclude in exclude_patterns):
|
1055
|
+
filtered.append(file_path)
|
1056
|
+
|
1057
|
+
return filtered[:max_files]
|
1058
|
+
except Exception:
|
1059
|
+
return []
|
1060
|
+
|
1061
|
+
def _format_comprehensive_samples(self, python_files: List[str], config_files: List[str],
|
1062
|
+
doc_files: List[str], project_path: str) -> str:
|
1063
|
+
"""Format comprehensive samples following PRD methodology."""
|
1064
|
+
content = []
|
1065
|
+
|
1066
|
+
# Python files
|
1067
|
+
if python_files:
|
1068
|
+
content.append("=== PYTHON CODE SAMPLES ===")
|
1069
|
+
content.append(self._format_sample_files(python_files, project_path))
|
1070
|
+
|
1071
|
+
# Config files
|
1072
|
+
if config_files:
|
1073
|
+
content.append("\n=== CONFIGURATION SAMPLES ===")
|
1074
|
+
content.append(self._format_sample_files(config_files, project_path))
|
1075
|
+
|
1076
|
+
# Documentation files
|
1077
|
+
if doc_files:
|
1078
|
+
content.append("\n=== DOCUMENTATION SAMPLES ===")
|
1079
|
+
content.append(self._format_sample_files(doc_files, project_path))
|
1080
|
+
|
1081
|
+
return "\n".join(content)
|
1082
|
+
|
1083
|
+
def _format_sample_files(self, files: List[str], project_path: str) -> str:
|
1084
|
+
"""Format sample files for analysis following PRD principles."""
|
1085
|
+
formatted = []
|
1086
|
+
for file_path in files:
|
1087
|
+
try:
|
1088
|
+
rel_path = os.path.relpath(file_path, project_path)
|
1089
|
+
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
1090
|
+
content = f.read()[:3000]
|
1091
|
+
formatted.append(f"File: {rel_path}\n{content}\n{'='*80}")
|
1092
|
+
except Exception:
|
1093
|
+
continue
|
1094
|
+
return "\n\n".join(formatted)
|
1095
|
+
|
1096
|
+
def _get_pattern_samples(self, project_path: str) -> str:
|
1097
|
+
"""Get representative samples for pattern extraction following PRD methodology."""
|
1098
|
+
samples = []
|
1099
|
+
|
1100
|
+
# Get diverse file types following PRD principles
|
1101
|
+
patterns = ["*.py", "test_*.py", "*_test.py", "conftest.py", "setup.py", "__init__.py", "pyproject.toml"]
|
1102
|
+
for pattern in patterns:
|
1103
|
+
files = self._get_filtered_files(project_path, pattern, 3)
|
1104
|
+
if files:
|
1105
|
+
samples.extend(files)
|
1106
|
+
|
1107
|
+
return self._format_sample_files(samples, project_path)
|
1108
|
+
|
1109
|
+
def create_quality_gates(self, requirements: List[str]) -> Dict[str, Any]:
|
1110
|
+
"""Create quality gates for validation following PRD methodology."""
|
1111
|
+
return {
|
1112
|
+
"syntax_gates": ["ruff check", "mypy"],
|
1113
|
+
"test_gates": ["pytest --cov=80"],
|
1114
|
+
"quality_gates": ["complexity check", "security scan"],
|
1115
|
+
"integration_gates": ["integration tests"],
|
1116
|
+
"requirements": requirements
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
def _save_context_engineering_results(self):
|
1120
|
+
"""Save all Context Engineering results following PRD methodology."""
|
1121
|
+
# Save comprehensive analysis
|
1122
|
+
with open(os.path.join(self.output_dir, "comprehensive_analysis.json"), "w") as f:
|
1123
|
+
json.dump(self.codebase_analysis, f, indent=2)
|
1124
|
+
|
1125
|
+
# Save pattern library
|
1126
|
+
with open(os.path.join(self.output_dir, "pattern_library.json"), "w") as f:
|
1127
|
+
json.dump(self.pattern_library, f, indent=2)
|
1128
|
+
|
1129
|
+
# Save validation framework
|
1130
|
+
with open(os.path.join(self.output_dir, "validation_framework.json"), "w") as f:
|
1131
|
+
json.dump(self.validation_framework, f, indent=2)
|
1132
|
+
|
1133
|
+
# Save all agent interactions summary
|
1134
|
+
with open(os.path.join(self.output_dir, "all_agent_interactions.json"), "w") as f:
|
1135
|
+
json.dump(self.agent_interactions, f, indent=2)
|
1136
|
+
|
1137
|
+
print(f"💾 Context Engineering results saved to: {self.output_dir}/")
|
1138
|
+
print(f"💾 Agent interactions ({len(self.agent_interactions)} total) saved to: agent_responses/")
|
1139
|
+
|
1140
|
+
def _generate_analysis_summary(self):
|
1141
|
+
"""Generate analysis summary following PRD methodology."""
|
1142
|
+
summary = f"""
|
1143
|
+
# Context Engineering Analysis Summary
|
1144
|
+
|
1145
|
+
## Analysis Method: {self.codebase_analysis.get('analysis_method', 'manual')}
|
1146
|
+
## Project Path: {self.project_path}
|
1147
|
+
## Analysis Completion: ✅
|
1148
|
+
## Total Agent Interactions: {len(self.agent_interactions)}
|
1149
|
+
|
1150
|
+
### Agent Interaction Tracking
|
1151
|
+
All {len(self.agent_interactions)} agent responses have been saved with complete traceability:
|
1152
|
+
- Individual response files in agent_responses/ directory
|
1153
|
+
- Complete interaction log in all_agent_interactions.json
|
1154
|
+
- Timestamps, prompts, responses, and metadata preserved
|
1155
|
+
|
1156
|
+
### Codebase Analysis
|
1157
|
+
- Method: {self.codebase_analysis.get('analysis_method', 'manual')}
|
1158
|
+
- Categories Analyzed: {len(self.codebase_analysis)}
|
1159
|
+
|
1160
|
+
### Pattern Library
|
1161
|
+
- Patterns Identified: {self.pattern_library.get('patterns_identified', 0)}
|
1162
|
+
- Implementation Patterns: Available
|
1163
|
+
- Testing Patterns: Available
|
1164
|
+
|
1165
|
+
### Validation Framework
|
1166
|
+
- Validation Types: {len(self.validation_framework)}
|
1167
|
+
- Quality Gates: Configured
|
1168
|
+
- Test Frameworks: Identified
|
1169
|
+
|
1170
|
+
### Context Documentation
|
1171
|
+
- Documentation Files: {len(self.context_documentation.get('readme_files', []))} README files
|
1172
|
+
- API Documentation: {len(self.context_documentation.get('api_docs', []))} API docs
|
1173
|
+
- Setup Documentation: Available
|
1174
|
+
|
1175
|
+
### Integration Points
|
1176
|
+
- Dependencies Analyzed: {len(getattr(self, 'integration_points', {}).get('dependencies', []))}
|
1177
|
+
- Configuration Points: Identified
|
1178
|
+
- External Services: Catalogued
|
1179
|
+
|
1180
|
+
## Ready for PRP Generation ✅
|
1181
|
+
The Context Engineering analysis is complete and ready to generate comprehensive
|
1182
|
+
Product Requirements Prompts following PRD template methodology.
|
1183
|
+
|
1184
|
+
## Complete Traceability ✅
|
1185
|
+
Every agent interaction has been saved for full audit trail and reproducibility.
|
1186
|
+
"""
|
1187
|
+
|
1188
|
+
with open(os.path.join(self.output_dir, "analysis_summary.md"), "w") as f:
|
1189
|
+
f.write(summary)
|
1190
|
+
|
1191
|
+
print(f"📋 Analysis summary saved following PRD methodology")
|
1192
|
+
print(f"📋 Complete agent interaction audit trail available")
|
1193
|
+
|
1194
|
+
def _perform_fallback_analysis(self):
|
1195
|
+
"""Perform fallback analysis if main analysis fails."""
|
1196
|
+
print("🔄 Performing fallback Context Engineering analysis...")
|
1197
|
+
|
1198
|
+
# Basic fallback analysis
|
1199
|
+
self.codebase_analysis = {"analysis_method": "fallback", "basic_structure": "analyzed"}
|
1200
|
+
self.pattern_library = {"patterns_identified": 0, "fallback_mode": True}
|
1201
|
+
self.validation_framework = {"basic_validation": ["pytest", "ruff check"]}
|
1202
|
+
|
1203
|
+
print("✅ Fallback analysis complete")
|
1204
|
+
|
1205
|
+
def generate_feature_prp(self, feature_request: str) -> str:
|
1206
|
+
"""Generate a comprehensive PRP for a specific feature request following PRD methodology."""
|
1207
|
+
print(f"🎯 Generating comprehensive PRP following PRD template for: {feature_request}")
|
1208
|
+
|
1209
|
+
# Use all available context following PRD methodology
|
1210
|
+
full_context = {
|
1211
|
+
"codebase_analysis": self.codebase_analysis,
|
1212
|
+
"pattern_library": self.pattern_library,
|
1213
|
+
"integration_points": getattr(self, 'integration_points', {}),
|
1214
|
+
"validation_framework": self.validation_framework,
|
1215
|
+
"context_documentation": self.context_documentation
|
1216
|
+
}
|
1217
|
+
|
1218
|
+
prp = self.generate_comprehensive_prp(feature_request, full_context)
|
1219
|
+
|
1220
|
+
# If auto_analyze was True and we have agent interactions, create combined response
|
1221
|
+
if hasattr(self, 'agent_interactions') and self.agent_interactions and len(self.agent_interactions) > 0:
|
1222
|
+
print(f"📊 Creating combined response with {len(self.agent_interactions)} agent interactions...")
|
1223
|
+
return self._generate_combined_analysis(feature_request)
|
1224
|
+
|
1225
|
+
# Otherwise just save and return the PRP
|
1226
|
+
safe_filename = feature_request.lower().replace(' ', '_').replace('-', '_')[:50]
|
1227
|
+
prp_path = os.path.join(self.output_dir, "PRPs", f"{safe_filename}.md")
|
1228
|
+
|
1229
|
+
with open(prp_path, "w") as f:
|
1230
|
+
f.write(prp)
|
1231
|
+
|
1232
|
+
print(f"📄 Comprehensive PRP following PRD template saved to: {prp_path}")
|
1233
|
+
return prp
|
1234
|
+
|
1235
|
+
def execute_prp(self, prp_file_path: str) -> Dict[str, Any]:
|
1236
|
+
"""Execute a PRP following PRD methodology (placeholder for future implementation)."""
|
1237
|
+
print(f"🚀 PRP execution capability - ready for implementation following PRD methodology")
|
1238
|
+
return {"status": "ready", "prp_file": prp_file_path}
|
1239
|
+
|
1240
|
+
def start(self, input_text: str) -> str:
|
1241
|
+
"""Start Context Engineering analysis with structured input parsing."""
|
1242
|
+
print(f"🚀 Starting Context Engineering analysis with structured input parsing...")
|
1243
|
+
|
1244
|
+
# First, parse the input to extract codebase and goal
|
1245
|
+
parsed_input = self._parse_structured_input(input_text)
|
1246
|
+
|
1247
|
+
codebase = parsed_input.get('codebase', '')
|
1248
|
+
goal = parsed_input.get('goal', '')
|
1249
|
+
|
1250
|
+
print(f"📊 Parsed Input:")
|
1251
|
+
print(f" 🔗 Codebase: {codebase}")
|
1252
|
+
print(f" 🎯 Goal: {goal}")
|
1253
|
+
|
1254
|
+
if codebase:
|
1255
|
+
if codebase.startswith(('http://', 'https://')):
|
1256
|
+
# Handle GitHub URL analysis with specific goal
|
1257
|
+
if 'github.com' in codebase:
|
1258
|
+
print(f"🌐 GitHub repository analysis focused on goal: {goal}")
|
1259
|
+
return self._analyze_github_with_goal(codebase, goal)
|
1260
|
+
else:
|
1261
|
+
print(f"🌐 General URL analysis focused on goal: {goal}")
|
1262
|
+
return self._analyze_url_with_goal(codebase, goal)
|
1263
|
+
else:
|
1264
|
+
# Handle local path analysis with specific goal
|
1265
|
+
if os.path.exists(codebase):
|
1266
|
+
print(f"📁 Local codebase analysis focused on goal: {goal}")
|
1267
|
+
self.project_path = codebase
|
1268
|
+
self._perform_context_engineering_analysis()
|
1269
|
+
return self._generate_goal_focused_analysis(codebase, goal)
|
1270
|
+
else:
|
1271
|
+
print(f"⚠️ Codebase path not found: {codebase}, using current project path")
|
1272
|
+
return self._generate_goal_focused_analysis(self.project_path, goal)
|
1273
|
+
else:
|
1274
|
+
# No specific codebase provided, use current directory
|
1275
|
+
print(f"📁 Using current directory with goal: {goal}")
|
1276
|
+
return self._generate_goal_focused_analysis(self.project_path, goal)
|
1277
|
+
|
1278
|
+
def _parse_structured_input(self, input_text: str) -> Dict[str, str]:
|
1279
|
+
"""Parse structured input to extract codebase and goal using a specialized agent."""
|
1280
|
+
print(" 🤖 Creating Structured Input Parser Agent...")
|
1281
|
+
|
1282
|
+
parser_agent = Agent(
|
1283
|
+
name="Structured Input Parser",
|
1284
|
+
role="Expert Input Analysis and Extraction Specialist",
|
1285
|
+
goal="Extract codebase URLs/paths and implementation goals from user input",
|
1286
|
+
instructions="""You are an expert at parsing user input to extract structured information.
|
1287
|
+
|
1288
|
+
From the provided input, extract:
|
1289
|
+
1. CODEBASE: Any GitHub URLs, repository paths, or local directory paths mentioned
|
1290
|
+
2. GOAL: The specific task, feature, or implementation goal the user wants to achieve
|
1291
|
+
|
1292
|
+
Rules for extraction:
|
1293
|
+
- Look for GitHub URLs (https://github.com/owner/repo)
|
1294
|
+
- Look for local paths (/path/to/directory)
|
1295
|
+
- Look for implementation goals (add authentication, implement feature X, etc.)
|
1296
|
+
- If multiple goals are mentioned, combine them into a coherent objective
|
1297
|
+
- If no explicit codebase is mentioned, return empty string for codebase
|
1298
|
+
- Always extract a meaningful goal even if it's implied
|
1299
|
+
|
1300
|
+
Return your analysis in this exact format:
|
1301
|
+
CODEBASE: [extracted codebase URL or path]
|
1302
|
+
GOAL: [extracted implementation goal]
|
1303
|
+
|
1304
|
+
Be precise and extract only what is explicitly mentioned or clearly implied.""",
|
1305
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
1306
|
+
verbose=getattr(self, 'verbose', True)
|
1307
|
+
)
|
1308
|
+
|
1309
|
+
prompt = f"""Parse this user input to extract codebase and goal:
|
1310
|
+
|
1311
|
+
USER INPUT: {input_text}
|
1312
|
+
|
1313
|
+
Extract the codebase (GitHub URL, repository path, or local directory) and the implementation goal.
|
1314
|
+
Return in the exact format specified in your instructions."""
|
1315
|
+
|
1316
|
+
print(" 🔍 Parsing structured input...")
|
1317
|
+
response = self._chat_with_agent_and_save(parser_agent, prompt, "INPUT_PARSING")
|
1318
|
+
|
1319
|
+
# Parse the response to extract codebase and goal
|
1320
|
+
parsed = self._extract_codebase_and_goal(response)
|
1321
|
+
|
1322
|
+
print(f" ✅ Input parsing complete: Codebase='{parsed.get('codebase', '')}', Goal='{parsed.get('goal', '')}'")
|
1323
|
+
return parsed
|
1324
|
+
|
1325
|
+
def _extract_codebase_and_goal(self, parser_response: str) -> Dict[str, str]:
|
1326
|
+
"""Extract codebase and goal from parser agent response."""
|
1327
|
+
codebase = ""
|
1328
|
+
goal = ""
|
1329
|
+
|
1330
|
+
lines = parser_response.strip().split('\n')
|
1331
|
+
for line in lines:
|
1332
|
+
line = line.strip()
|
1333
|
+
if line.startswith('CODEBASE:'):
|
1334
|
+
codebase = line.replace('CODEBASE:', '').strip()
|
1335
|
+
elif line.startswith('GOAL:'):
|
1336
|
+
goal = line.replace('GOAL:', '').strip()
|
1337
|
+
|
1338
|
+
# Fallback parsing if format wasn't followed exactly
|
1339
|
+
if not codebase or not goal:
|
1340
|
+
# Look for GitHub URLs
|
1341
|
+
import re
|
1342
|
+
github_pattern = r'https://github\.com/[^\s]+'
|
1343
|
+
github_matches = re.findall(github_pattern, parser_response)
|
1344
|
+
if github_matches and not codebase:
|
1345
|
+
codebase = github_matches[0]
|
1346
|
+
|
1347
|
+
# Look for common goal keywords
|
1348
|
+
goal_keywords = ['add', 'implement', 'create', 'build', 'develop', 'need to', 'authentication', 'feature']
|
1349
|
+
if not goal:
|
1350
|
+
for keyword in goal_keywords:
|
1351
|
+
if keyword.lower() in parser_response.lower():
|
1352
|
+
# Extract sentence containing the keyword
|
1353
|
+
sentences = parser_response.split('.')
|
1354
|
+
for sentence in sentences:
|
1355
|
+
if keyword.lower() in sentence.lower():
|
1356
|
+
goal = sentence.strip()
|
1357
|
+
break
|
1358
|
+
if goal:
|
1359
|
+
break
|
1360
|
+
|
1361
|
+
return {
|
1362
|
+
'codebase': codebase,
|
1363
|
+
'goal': goal or "Comprehensive codebase analysis and context generation"
|
1364
|
+
}
|
1365
|
+
|
1366
|
+
def _analyze_github_with_goal(self, github_url: str, goal: str) -> str:
|
1367
|
+
"""Analyze GitHub repository with gitingest, focusing on specific goal."""
|
1368
|
+
print(f" 🌐 Analyzing GitHub repository with goal focus...")
|
1369
|
+
|
1370
|
+
# Step 1: Get complete repository structure and file listing
|
1371
|
+
print(" 📊 Step 1: Getting complete repository structure...")
|
1372
|
+
repo_structure = self._get_repository_structure(github_url)
|
1373
|
+
|
1374
|
+
# Step 2: Have agent select relevant files based on goal
|
1375
|
+
print(" 🎯 Step 2: Agent selecting relevant files for goal...")
|
1376
|
+
selected_files = self._select_relevant_files_for_goal(repo_structure, goal, github_url)
|
1377
|
+
|
1378
|
+
# Step 3: Analyze only selected files with gitingest
|
1379
|
+
print(" 📋 Step 3: Analyzing selected files with gitingest...")
|
1380
|
+
return self._analyze_selected_files(github_url, goal, selected_files)
|
1381
|
+
|
1382
|
+
def _get_repository_structure(self, github_url: str) -> Dict[str, Any]:
|
1383
|
+
"""Get complete repository structure with detailed file metadata."""
|
1384
|
+
self.log_debug("Starting repository structure analysis", github_url=github_url)
|
1385
|
+
|
1386
|
+
try:
|
1387
|
+
# Step 1: Get basic file structure
|
1388
|
+
print(" 📋 Getting basic repository structure...")
|
1389
|
+
self.log_debug("Getting basic repository structure")
|
1390
|
+
basic_structure = self._get_basic_structure(github_url)
|
1391
|
+
self.log_debug("Basic structure obtained", file_count=basic_structure.get('file_count', 0))
|
1392
|
+
|
1393
|
+
# Step 2: Get enhanced metadata for key files
|
1394
|
+
print(" 🔍 Extracting detailed file metadata...")
|
1395
|
+
self.log_debug("Starting enhanced metadata extraction")
|
1396
|
+
enhanced_structure = self._get_enhanced_file_metadata(github_url, basic_structure)
|
1397
|
+
self.log_debug("Enhanced metadata extraction complete",
|
1398
|
+
enhanced=enhanced_structure.get('enhanced', False),
|
1399
|
+
metadata_length=len(enhanced_structure.get('file_metadata', '')))
|
1400
|
+
|
1401
|
+
return enhanced_structure
|
1402
|
+
|
1403
|
+
except Exception as e:
|
1404
|
+
print(f" ⚠️ Repository structure analysis failed: {e}")
|
1405
|
+
self.log_debug("Repository structure analysis failed", error=str(e))
|
1406
|
+
return {
|
1407
|
+
"method": "failed",
|
1408
|
+
"structure": "Unable to retrieve repository structure",
|
1409
|
+
"file_count": 0,
|
1410
|
+
"success": False
|
1411
|
+
}
|
1412
|
+
|
1413
|
+
def _get_basic_structure(self, github_url: str) -> Dict[str, Any]:
|
1414
|
+
"""Get basic repository file structure."""
|
1415
|
+
try:
|
1416
|
+
# Try using gitingest with structure-only flag first
|
1417
|
+
result = subprocess.run(
|
1418
|
+
["gitingest", github_url, "--tree-only", "--output", "-"],
|
1419
|
+
capture_output=True,
|
1420
|
+
text=True,
|
1421
|
+
timeout=60
|
1422
|
+
)
|
1423
|
+
|
1424
|
+
if result.returncode == 0:
|
1425
|
+
return {
|
1426
|
+
"method": "gitingest_tree",
|
1427
|
+
"structure": result.stdout,
|
1428
|
+
"file_count": result.stdout.count('\n'),
|
1429
|
+
"success": True
|
1430
|
+
}
|
1431
|
+
else:
|
1432
|
+
print(f" ⚠️ Gitingest tree failed, using GitHub API...")
|
1433
|
+
return self._get_structure_fallback(github_url)
|
1434
|
+
|
1435
|
+
except (subprocess.TimeoutExpired, FileNotFoundError) as e:
|
1436
|
+
print(f" ⚠️ Gitingest not available: {e}, using GitHub API...")
|
1437
|
+
return self._get_structure_fallback(github_url)
|
1438
|
+
|
1439
|
+
def _get_enhanced_file_metadata(self, github_url: str, basic_structure: Dict[str, Any]) -> Dict[str, Any]:
|
1440
|
+
"""Extract enhanced metadata including functions, classes, and docstrings."""
|
1441
|
+
print(" 🔍 Extracting detailed file metadata...")
|
1442
|
+
|
1443
|
+
# Step 1: Get raw content from repository
|
1444
|
+
sample_content = self._get_repository_sample_content(github_url, basic_structure.get('structure', ''))
|
1445
|
+
|
1446
|
+
# Step 2: Parse the content to extract structured metadata
|
1447
|
+
parsed_metadata = self._parse_code_metadata(sample_content, basic_structure)
|
1448
|
+
|
1449
|
+
# DEBUG: Check what metadata was actually parsed
|
1450
|
+
print(f" 🔍 DEBUG: Parsed metadata length: {len(parsed_metadata)} chars")
|
1451
|
+
print(f" 🔍 DEBUG: Metadata preview: {parsed_metadata[:200]}...")
|
1452
|
+
|
1453
|
+
# Step 3: Create enhanced structure with parsed metadata
|
1454
|
+
enhanced_structure = {
|
1455
|
+
"method": "enhanced_metadata",
|
1456
|
+
"basic_structure": basic_structure,
|
1457
|
+
"file_metadata": parsed_metadata,
|
1458
|
+
"file_count": basic_structure.get('file_count', 0),
|
1459
|
+
"success": True,
|
1460
|
+
"enhanced": True
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
print(f" ✅ Enhanced structure created with metadata length: {len(enhanced_structure.get('file_metadata', ''))}")
|
1464
|
+
return enhanced_structure
|
1465
|
+
|
1466
|
+
def _parse_code_metadata(self, sample_content: str, basic_structure: Dict[str, Any]) -> str:
|
1467
|
+
"""Parse code content to extract functions, classes, docstrings, and other metadata."""
|
1468
|
+
print(" 🤖 Creating Code Metadata Parser Agent...")
|
1469
|
+
|
1470
|
+
# Check if we have actual content to parse
|
1471
|
+
if not sample_content or len(sample_content.strip()) < 100:
|
1472
|
+
print(" ⚠️ No substantial content available for metadata parsing")
|
1473
|
+
return """
|
1474
|
+
METADATA EXTRACTION STATUS: Limited content available
|
1475
|
+
|
1476
|
+
BASIC FILE STRUCTURE ANALYSIS:
|
1477
|
+
- Repository appears to contain Python, JavaScript, and configuration files
|
1478
|
+
- Authentication-related files likely in: auth/, user/, login/, security/ directories
|
1479
|
+
- Configuration files: settings.py, config.py, .env files
|
1480
|
+
- API files: api/, routes/, endpoints/ directories
|
1481
|
+
|
1482
|
+
RECOMMENDED FILE SELECTION APPROACH:
|
1483
|
+
- Look for files with 'auth', 'login', 'user', 'security' in names
|
1484
|
+
- Include main application files (app.py, main.py, server.py)
|
1485
|
+
- Include configuration files (settings.py, config.py)
|
1486
|
+
- Include API route files
|
1487
|
+
- Include requirements.txt, package.json for dependencies
|
1488
|
+
|
1489
|
+
Note: Detailed function/class metadata not available due to content access limitations.
|
1490
|
+
"""
|
1491
|
+
|
1492
|
+
# Truncate sample content if too large to prevent context overflow
|
1493
|
+
max_content_length = 60000 # Increased from 50K to 60K for better metadata
|
1494
|
+
if len(sample_content) > max_content_length:
|
1495
|
+
sample_content = sample_content[:max_content_length] + "\n\n... [Content truncated for analysis] ..."
|
1496
|
+
print(f" 📏 Sample content truncated to {max_content_length} chars for metadata parsing")
|
1497
|
+
|
1498
|
+
metadata_parser = Agent(
|
1499
|
+
name="Code Metadata Parser",
|
1500
|
+
role="Expert Code Analysis and Metadata Extraction Specialist",
|
1501
|
+
goal="Parse code content to extract detailed metadata including functions, classes, and docstrings",
|
1502
|
+
instructions="""You are an expert at parsing code content and extracting structured metadata.
|
1503
|
+
|
1504
|
+
From the provided code content, extract detailed metadata for each file:
|
1505
|
+
|
1506
|
+
FOR PYTHON FILES (.py):
|
1507
|
+
- FUNCTIONS: Extract function names with their docstrings and parameters
|
1508
|
+
Format: "function_name(params): docstring_summary"
|
1509
|
+
- CLASSES: Extract class names with their docstrings and key methods
|
1510
|
+
Format: "ClassName: docstring_summary, methods: [method1, method2]"
|
1511
|
+
- MODULE_DOCSTRING: Top-level file description
|
1512
|
+
- KEY_IMPORTS: Important imports and dependencies
|
1513
|
+
- DECORATORS: Common decorators used (@app.route, @property, etc.)
|
1514
|
+
|
1515
|
+
FOR JAVASCRIPT/TYPESCRIPT FILES (.js, .ts, .jsx, .tsx):
|
1516
|
+
- FUNCTIONS: Function names and purposes
|
1517
|
+
- CLASSES/COMPONENTS: React components or ES6 classes
|
1518
|
+
- EXPORTS: What the file exports
|
1519
|
+
- IMPORTS: Key dependencies
|
1520
|
+
|
1521
|
+
FOR CONFIGURATION FILES (.json, .toml, .yaml, .env):
|
1522
|
+
- PURPOSE: What this configuration controls
|
1523
|
+
- KEY_SECTIONS: Main configuration categories
|
1524
|
+
- DEPENDENCIES: Packages or services configured
|
1525
|
+
|
1526
|
+
FOR DOCUMENTATION FILES (.md, .rst, .txt):
|
1527
|
+
- TITLE: Document title/purpose
|
1528
|
+
- SECTIONS: Main topics covered
|
1529
|
+
- API_DOCS: If it documents APIs or functions
|
1530
|
+
|
1531
|
+
**CRITICAL**: Return structured data that clearly shows:
|
1532
|
+
1. File name and type
|
1533
|
+
2. Functions/classes with their purposes
|
1534
|
+
3. Key imports and dependencies
|
1535
|
+
4. Configuration purposes
|
1536
|
+
5. Documentation topics
|
1537
|
+
|
1538
|
+
Make the output easy for a file selection agent to understand which files contain what functionality.""",
|
1539
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
1540
|
+
verbose=getattr(self, 'verbose', True)
|
1541
|
+
)
|
1542
|
+
|
1543
|
+
structure_text = basic_structure.get('structure', '')
|
1544
|
+
|
1545
|
+
prompt = f"""Parse this repository content to extract detailed metadata:
|
1546
|
+
|
1547
|
+
REPOSITORY STRUCTURE:
|
1548
|
+
{structure_text}
|
1549
|
+
|
1550
|
+
REPOSITORY CONTENT TO PARSE:
|
1551
|
+
{sample_content}
|
1552
|
+
|
1553
|
+
Extract structured metadata for each file showing:
|
1554
|
+
- Functions and their purposes
|
1555
|
+
- Classes and their purposes
|
1556
|
+
- Key imports and dependencies
|
1557
|
+
- Configuration sections
|
1558
|
+
- Documentation topics
|
1559
|
+
|
1560
|
+
Focus on creating clear, structured metadata that will help with intelligent file selection."""
|
1561
|
+
|
1562
|
+
print(" 🔍 Code Parser extracting functions, classes, and docstrings...")
|
1563
|
+
response = self._chat_with_agent_and_save(metadata_parser, prompt, "CODE_METADATA_PARSING")
|
1564
|
+
|
1565
|
+
# Ensure we return the actual parsed metadata response
|
1566
|
+
print(f" ✅ Extracted metadata length: {len(response)} characters")
|
1567
|
+
if len(response) < 100:
|
1568
|
+
print(" ⚠️ Warning: Very short metadata response - may indicate parsing issues")
|
1569
|
+
|
1570
|
+
return response
|
1571
|
+
|
1572
|
+
def _get_repository_sample_content(self, github_url: str, structure_text: str) -> str:
|
1573
|
+
"""Get actual code content from key repository files for metadata extraction."""
|
1574
|
+
try:
|
1575
|
+
print(" 📋 Getting actual code content from repository files...")
|
1576
|
+
|
1577
|
+
# Validate GitHub token first
|
1578
|
+
github_token = os.getenv('GITHUB_TOKEN')
|
1579
|
+
if github_token:
|
1580
|
+
print(f" 🔑 GitHub token found: {github_token[:8]}...{github_token[-4:]} (length: {len(github_token)})")
|
1581
|
+
|
1582
|
+
# Validate token format
|
1583
|
+
import re
|
1584
|
+
github_pat_pattern = r"^(?:gh[pousr]_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59})$"
|
1585
|
+
if not re.match(github_pat_pattern, github_token):
|
1586
|
+
print(" ⚠️ Token format looks invalid - may cause authentication issues")
|
1587
|
+
print(" 📝 Valid formats: ghp_xxxx or github_pat_xxxx")
|
1588
|
+
else:
|
1589
|
+
print(" ✅ Token format is valid")
|
1590
|
+
|
1591
|
+
# Quick test of token validity
|
1592
|
+
try:
|
1593
|
+
import requests
|
1594
|
+
test_headers = {'Authorization': f'Bearer {github_token}'}
|
1595
|
+
test_response = requests.get('https://api.github.com/user', headers=test_headers, timeout=5)
|
1596
|
+
if test_response.status_code == 200:
|
1597
|
+
user_data = test_response.json()
|
1598
|
+
print(f" ✅ Token is valid for user: {user_data.get('login')}")
|
1599
|
+
elif test_response.status_code == 401:
|
1600
|
+
print(" ❌ Token is invalid or expired!")
|
1601
|
+
print(" 📝 Generate new token at: https://github.com/settings/tokens")
|
1602
|
+
elif test_response.status_code == 403:
|
1603
|
+
print(" ❌ Token lacks required permissions!")
|
1604
|
+
print(" 📝 Required scopes: 'repo' or 'public_repo'")
|
1605
|
+
else:
|
1606
|
+
print(f" ⚠️ Unexpected token test result: {test_response.status_code}")
|
1607
|
+
except Exception as e:
|
1608
|
+
print(f" ⚠️ Could not validate token: {e}")
|
1609
|
+
else:
|
1610
|
+
print(" ⚠️ No GitHub token found - may hit rate limits")
|
1611
|
+
|
1612
|
+
# Try gitingest Python package first for actual content
|
1613
|
+
try:
|
1614
|
+
from gitingest import ingest
|
1615
|
+
print(" 🔧 Using gitingest Python package to fetch actual code content...")
|
1616
|
+
|
1617
|
+
# Get comprehensive content including actual code
|
1618
|
+
# Use better limits for codebase overview while avoiding context overflow
|
1619
|
+
summary, tree, content = ingest(
|
1620
|
+
github_url,
|
1621
|
+
max_file_size=10000, # 10KB per file for better overview (was 5KB)
|
1622
|
+
include_patterns={'*.py', '*.js', '*.ts', '*.json', '*.md', '*.yaml', '*.toml'}, # Include more file types for overview
|
1623
|
+
exclude_patterns={'*test*', '*__pycache__*', '*.pyc', '*migrations*', '*node_modules*', '*.min.js', '*.bundle.js'}, # Exclude generated/test files
|
1624
|
+
token=github_token
|
1625
|
+
)
|
1626
|
+
|
1627
|
+
# More conservative truncation to ensure we keep actual code content
|
1628
|
+
max_content_for_metadata = 80000 # 80K chars for metadata extraction
|
1629
|
+
if len(content) > max_content_for_metadata:
|
1630
|
+
# Keep the beginning (which has most file headers) and truncate the rest
|
1631
|
+
content = content[:max_content_for_metadata] + "\n\n... [Content truncated but metadata extracted from available content] ..."
|
1632
|
+
print(f" 📏 Content size: {len(content)}, keeping first {max_content_for_metadata} chars for metadata")
|
1633
|
+
|
1634
|
+
# Return actual code content for parsing - this is crucial for metadata extraction
|
1635
|
+
return f"REPOSITORY SUMMARY:\n{summary}\n\nFILE TREE:\n{tree}\n\nACTUAL CODE CONTENT:\n{content}"
|
1636
|
+
|
1637
|
+
except ImportError:
|
1638
|
+
print(" ⚠️ gitingest Python package not available, using command line...")
|
1639
|
+
|
1640
|
+
# Fallback to command line gitingest for actual content
|
1641
|
+
result = subprocess.run(
|
1642
|
+
["gitingest", github_url, "--output", "-"],
|
1643
|
+
capture_output=True,
|
1644
|
+
text=True,
|
1645
|
+
timeout=120
|
1646
|
+
)
|
1647
|
+
|
1648
|
+
if result.returncode == 0:
|
1649
|
+
# This should contain actual file contents, not just structure
|
1650
|
+
return result.stdout
|
1651
|
+
else:
|
1652
|
+
print(f" ⚠️ Gitingest command failed: {result.stderr}")
|
1653
|
+
return self._get_github_sample_files(github_url, structure_text)
|
1654
|
+
|
1655
|
+
except Exception as e:
|
1656
|
+
print(f" ⚠️ Sample content extraction failed: {e}")
|
1657
|
+
return self._get_github_sample_files(github_url, structure_text)
|
1658
|
+
|
1659
|
+
def _get_github_sample_files(self, github_url: str, structure_text: str) -> str:
|
1660
|
+
"""Fallback to get sample file contents directly from GitHub."""
|
1661
|
+
try:
|
1662
|
+
print(" 🔄 Attempting to fetch sample file contents from GitHub...")
|
1663
|
+
|
1664
|
+
# Extract repo info
|
1665
|
+
repo_parts = github_url.replace('https://github.com/', '').strip('/')
|
1666
|
+
|
1667
|
+
# Check for GitHub token for better rate limits
|
1668
|
+
github_token = os.getenv('GITHUB_TOKEN')
|
1669
|
+
headers = {}
|
1670
|
+
if github_token:
|
1671
|
+
headers['Authorization'] = f'Bearer {github_token}' # Use Bearer instead of token
|
1672
|
+
print(" 🔑 Using GitHub token for enhanced rate limits")
|
1673
|
+
|
1674
|
+
# Select important files to fetch content from
|
1675
|
+
important_files = []
|
1676
|
+
for line in structure_text.split('\n'):
|
1677
|
+
line = line.strip()
|
1678
|
+
if line and any(ext in line for ext in ['.py', '.js', '.ts', '.json', '.md']):
|
1679
|
+
# Prioritize likely important files
|
1680
|
+
if any(keyword in line.lower() for keyword in [
|
1681
|
+
'main', 'app', 'server', 'index', 'config', 'setup',
|
1682
|
+
'auth', 'user', 'login', 'api', 'route', 'model'
|
1683
|
+
]):
|
1684
|
+
important_files.append(line)
|
1685
|
+
if len(important_files) >= 10: # Limit to avoid too many API calls
|
1686
|
+
break
|
1687
|
+
|
1688
|
+
# Fetch content from important files
|
1689
|
+
file_contents = []
|
1690
|
+
for file_path in important_files[:5]: # Limit to 5 files to avoid rate limits
|
1691
|
+
try:
|
1692
|
+
api_url = f"https://api.github.com/repos/{repo_parts}/contents/{file_path}"
|
1693
|
+
|
1694
|
+
import urllib.request
|
1695
|
+
import json
|
1696
|
+
import base64
|
1697
|
+
|
1698
|
+
request = urllib.request.Request(api_url, headers=headers)
|
1699
|
+
|
1700
|
+
with urllib.request.urlopen(request) as response:
|
1701
|
+
file_data = json.loads(response.read())
|
1702
|
+
|
1703
|
+
if file_data.get('content') and file_data.get('encoding') == 'base64':
|
1704
|
+
content = base64.b64decode(file_data['content']).decode('utf-8')
|
1705
|
+
file_contents.append(f"\n--- FILE: {file_path} ---\n{content[:2000]}") # First 2000 chars
|
1706
|
+
|
1707
|
+
except Exception as e:
|
1708
|
+
print(f" ⚠️ Failed to fetch {file_path}: {e}")
|
1709
|
+
continue
|
1710
|
+
|
1711
|
+
if file_contents:
|
1712
|
+
return f"SAMPLE FILE CONTENTS:\n{''.join(file_contents)}"
|
1713
|
+
else:
|
1714
|
+
return "No sample content available - file fetching failed"
|
1715
|
+
|
1716
|
+
except Exception as e:
|
1717
|
+
print(f" ⚠️ GitHub sample file fetching failed: {e}")
|
1718
|
+
return "No sample content available - GitHub API failed"
|
1719
|
+
|
1720
|
+
def _select_relevant_files_for_goal(self, repo_structure: Dict[str, Any], goal: str, github_url: str) -> List[str]:
|
1721
|
+
"""Have an agent intelligently select relevant files based on goal and enhanced metadata."""
|
1722
|
+
print(" 🤖 Creating Enhanced File Selection Agent...")
|
1723
|
+
|
1724
|
+
file_selector = Agent(
|
1725
|
+
name="Enhanced File Selection Agent",
|
1726
|
+
role="Expert File Selection Specialist with Metadata Analysis",
|
1727
|
+
goal=f"Select most relevant files for implementing: {goal}",
|
1728
|
+
instructions=f"""You are an expert at selecting the most relevant files from a repository for a specific goal: {goal}
|
1729
|
+
|
1730
|
+
You have access to enhanced metadata including:
|
1731
|
+
- File structure and organization
|
1732
|
+
- Functions and classes in each file
|
1733
|
+
- Docstrings and descriptions
|
1734
|
+
- Configuration purposes
|
1735
|
+
- Documentation coverage
|
1736
|
+
- Import dependencies
|
1737
|
+
|
1738
|
+
CORE FILES TO ALWAYS INCLUDE (if they exist):
|
1739
|
+
- README.md, setup.py, pyproject.toml, package.json, requirements.txt
|
1740
|
+
- Main entry points: main.py, app.py, index.js, server.py
|
1741
|
+
- Configuration files: config.py, settings.py, .env.example
|
1742
|
+
- Key documentation: CONTRIBUTING.md, API.md, docs/
|
1743
|
+
|
1744
|
+
GOAL-SPECIFIC SELECTION for "{goal}":
|
1745
|
+
- Files with functions/classes directly related to goal keywords
|
1746
|
+
- Files mentioned in docstrings related to the goal
|
1747
|
+
- Configuration files that would need updates for the goal
|
1748
|
+
- Test files that cover similar functionality
|
1749
|
+
- Documentation that explains related features
|
1750
|
+
- Dependencies and imports relevant to the goal
|
1751
|
+
|
1752
|
+
ENHANCED SELECTION CRITERIA:
|
1753
|
+
1. Core project files (always include)
|
1754
|
+
2. Files with functions/classes matching goal keywords
|
1755
|
+
3. Files with docstrings mentioning goal-related concepts
|
1756
|
+
4. Files in directories related to the goal
|
1757
|
+
5. Configuration files that would be affected
|
1758
|
+
6. Test files for related functionality
|
1759
|
+
7. Documentation explaining similar features
|
1760
|
+
8. Dependency files showing required packages
|
1761
|
+
|
1762
|
+
METADATA-BASED PRIORITIZATION:
|
1763
|
+
- High Priority: Files with functions/classes directly implementing goal features
|
1764
|
+
- Medium Priority: Files with related functionality or configuration
|
1765
|
+
- Low Priority: Files with tangential connections
|
1766
|
+
|
1767
|
+
Use the enhanced metadata to make intelligent decisions about file relevance.
|
1768
|
+
|
1769
|
+
Return ONLY a Python list of file paths, like:
|
1770
|
+
["README.md", "src/auth/login.py", "config/settings.py", ...]
|
1771
|
+
|
1772
|
+
Maximum 50 files for efficient analysis.""",
|
1773
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
1774
|
+
verbose=getattr(self, 'verbose', True)
|
1775
|
+
)
|
1776
|
+
|
1777
|
+
# Prepare enhanced structure information
|
1778
|
+
basic_structure = repo_structure.get('basic_structure', {}).get('structure', '')
|
1779
|
+
file_metadata = repo_structure.get('file_metadata', '')
|
1780
|
+
file_count = repo_structure.get('file_count', 0)
|
1781
|
+
|
1782
|
+
# DEBUG: Check what metadata is actually available
|
1783
|
+
print(f" 🔍 DEBUG: Received metadata length: {len(file_metadata)} chars")
|
1784
|
+
print(f" 🔍 DEBUG: Enhanced flag: {repo_structure.get('enhanced', False)}")
|
1785
|
+
print(f" 🔍 DEBUG: Metadata preview: {file_metadata[:200] if file_metadata else 'NO METADATA'}...")
|
1786
|
+
|
1787
|
+
prompt = f"""Select the most relevant files for goal: {goal}
|
1788
|
+
|
1789
|
+
REPOSITORY: {github_url}
|
1790
|
+
GOAL: {goal}
|
1791
|
+
TOTAL FILES: {file_count}
|
1792
|
+
ENHANCED METADATA AVAILABLE: {repo_structure.get('enhanced', False)}
|
1793
|
+
|
1794
|
+
REPOSITORY STRUCTURE:
|
1795
|
+
{basic_structure}
|
1796
|
+
|
1797
|
+
PARSED CODE METADATA (Functions, Classes, Docstrings):
|
1798
|
+
{file_metadata}
|
1799
|
+
|
1800
|
+
Based on the goal "{goal}" and the parsed metadata showing actual functions/classes/docstrings:"""
|
1801
|
+
|
1802
|
+
# DEBUG: Verify f-string substitution worked
|
1803
|
+
print(f" 🔍 DEBUG: Variable values before f-string:")
|
1804
|
+
print(f" 🔍 DEBUG: goal = '{goal}'")
|
1805
|
+
print(f" 🔍 DEBUG: file_count = {file_count}")
|
1806
|
+
print(f" 🔍 DEBUG: enhanced = {repo_structure.get('enhanced', False)}")
|
1807
|
+
print(f" 🔍 DEBUG: file_metadata is None: {file_metadata is None}")
|
1808
|
+
print(f" 🔍 DEBUG: file_metadata is empty string: {repr(file_metadata == '')}")
|
1809
|
+
print(f" 🔍 DEBUG: type(file_metadata) = {type(file_metadata)}")
|
1810
|
+
|
1811
|
+
# Continue building the prompt
|
1812
|
+
prompt += """
|
1813
|
+
|
1814
|
+
INTELLIGENT SELECTION CRITERIA:
|
1815
|
+
1. **FUNCTION-LEVEL MATCHING**: Select files that contain functions directly related to "{goal}"
|
1816
|
+
2. **CLASS-LEVEL MATCHING**: Select files with classes that handle "{goal}" functionality
|
1817
|
+
3. **DOCSTRING MATCHING**: Select files whose docstrings mention "{goal}" concepts
|
1818
|
+
4. **DEPENDENCY MATCHING**: Select files that import libraries relevant to "{goal}"
|
1819
|
+
5. **CONFIGURATION MATCHING**: Select config files that would need updates for "{goal}"
|
1820
|
+
6. **TEST MATCHING**: Select test files that cover "{goal}" functionality
|
1821
|
+
7. **DOCUMENTATION MATCHING**: Select docs that explain "{goal}" or similar features
|
1822
|
+
|
1823
|
+
EXAMPLES FOR AUTHENTICATION GOAL:
|
1824
|
+
- Files with functions like: login(), authenticate(), verify_token()
|
1825
|
+
- Files with classes like: User, AuthManager, SecurityHandler
|
1826
|
+
- Files with docstrings mentioning: "authentication", "login", "security", "user management"
|
1827
|
+
- Files importing: jwt, bcrypt, passport, oauth libraries
|
1828
|
+
- Config files with: security settings, database user models, API keys
|
1829
|
+
- Test files testing: login flows, authentication endpoints
|
1830
|
+
- Docs explaining: API authentication, user management
|
1831
|
+
|
1832
|
+
Use the detailed metadata to make precise selections based on actual code content, not just filenames.
|
1833
|
+
|
1834
|
+
Return as a Python list: ["file1.py", "file2.js", ...]
|
1835
|
+
Maximum 50 files.""".format(goal=goal)
|
1836
|
+
|
1837
|
+
print(" 🔍 Enhanced File Selector analyzing metadata...")
|
1838
|
+
|
1839
|
+
# DEBUG: Show the actual prompt being sent
|
1840
|
+
print(f" 🔍 DEBUG: Full prompt length: {len(prompt)} chars")
|
1841
|
+
print(f" 🔍 DEBUG: Prompt preview (first 500 chars):")
|
1842
|
+
print(f" {prompt[:500]}...")
|
1843
|
+
|
1844
|
+
# DEBUG: Check specifically for metadata in the prompt
|
1845
|
+
metadata_start = prompt.find("PARSED CODE METADATA")
|
1846
|
+
if metadata_start != -1:
|
1847
|
+
metadata_section = prompt[metadata_start:metadata_start+1000]
|
1848
|
+
print(f" 🔍 DEBUG: Found metadata section at position {metadata_start}:")
|
1849
|
+
print(f" {metadata_section}...")
|
1850
|
+
else:
|
1851
|
+
print(f" ❌ DEBUG: 'PARSED CODE METADATA' section NOT FOUND in prompt!")
|
1852
|
+
|
1853
|
+
# DEBUG: Check the actual file_metadata variable content
|
1854
|
+
print(f" 🔍 DEBUG: file_metadata variable length: {len(file_metadata)}")
|
1855
|
+
print(f" 🔍 DEBUG: file_metadata first 300 chars: {file_metadata[:300]}...")
|
1856
|
+
|
1857
|
+
response = self._chat_with_agent_and_save(file_selector, prompt, "ENHANCED_FILE_SELECTION")
|
1858
|
+
|
1859
|
+
# Parse the response to extract file list
|
1860
|
+
selected_files = self._parse_file_list_from_response(response)
|
1861
|
+
|
1862
|
+
print(f" ✅ Selected {len(selected_files)} relevant files from {file_count} total files using enhanced metadata")
|
1863
|
+
return selected_files
|
1864
|
+
|
1865
|
+
def _parse_file_list_from_response(self, response: str) -> List[str]:
|
1866
|
+
"""Parse file list from agent response."""
|
1867
|
+
import re
|
1868
|
+
|
1869
|
+
# Look for Python list pattern
|
1870
|
+
list_pattern = r'\[(.*?)\]'
|
1871
|
+
matches = re.findall(list_pattern, response, re.DOTALL)
|
1872
|
+
|
1873
|
+
if matches:
|
1874
|
+
# Take the first list found
|
1875
|
+
list_content = matches[0]
|
1876
|
+
# Extract quoted strings
|
1877
|
+
file_pattern = r'["\']([^"\']+)["\']'
|
1878
|
+
files = re.findall(file_pattern, list_content)
|
1879
|
+
return files[:50] # Limit to 50 files
|
1880
|
+
|
1881
|
+
# Fallback: look for lines that look like file paths
|
1882
|
+
lines = response.split('\n')
|
1883
|
+
files = []
|
1884
|
+
for line in lines:
|
1885
|
+
line = line.strip()
|
1886
|
+
if line and ('.' in line or '/' in line) and not line.startswith('#'):
|
1887
|
+
# Remove quotes and clean up
|
1888
|
+
clean_line = line.strip('"\'`- ')
|
1889
|
+
if clean_line and not clean_line.startswith('//'):
|
1890
|
+
files.append(clean_line)
|
1891
|
+
if len(files) >= 50:
|
1892
|
+
break
|
1893
|
+
|
1894
|
+
return files
|
1895
|
+
|
1896
|
+
def _analyze_selected_files(self, github_url: str, goal: str, selected_files: List[str]) -> str:
|
1897
|
+
"""Analyze only the selected relevant files with gitingest."""
|
1898
|
+
print(f" 🔍 Creating Focused Repository Analyzer for {len(selected_files)} files...")
|
1899
|
+
|
1900
|
+
# Try to get content for selected files only
|
1901
|
+
if selected_files:
|
1902
|
+
focused_content = self._get_focused_content(github_url, selected_files)
|
1903
|
+
else:
|
1904
|
+
print(" ⚠️ No files selected, falling back to full analysis...")
|
1905
|
+
focused_content = self._run_gitingest_for_github(github_url)
|
1906
|
+
|
1907
|
+
focused_analyzer = Agent(
|
1908
|
+
name="Focused Repository Analyst",
|
1909
|
+
role="Expert Repository Analysis Specialist with Focused File Selection",
|
1910
|
+
goal=f"Analyze selected repository files specifically for implementing: {goal}",
|
1911
|
+
instructions=f"""You are analyzing carefully selected files from a GitHub repository with this specific goal: {goal}
|
1912
|
+
|
1913
|
+
These files were intelligently selected to be most relevant for: {goal}
|
1914
|
+
|
1915
|
+
Focus your analysis on:
|
1916
|
+
1. GOAL-RELEVANT COMPONENTS: How these files relate to {goal}
|
1917
|
+
2. ARCHITECTURE: How {goal} should fit into the existing architecture
|
1918
|
+
3. PATTERNS: Existing implementation patterns to follow for {goal}
|
1919
|
+
4. DEPENDENCIES: What dependencies are needed for {goal}
|
1920
|
+
5. TESTING: How to test the {goal} implementation
|
1921
|
+
6. INTEGRATION: How {goal} integrates with existing features
|
1922
|
+
7. CONFIGURATION: Setup and configuration needs for {goal}
|
1923
|
+
8. EXAMPLES: Similar features that can guide {goal} implementation
|
1924
|
+
|
1925
|
+
Since these files were pre-selected for relevance, provide deep analysis of how each contributes to implementing: {goal}""",
|
1926
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
1927
|
+
verbose=getattr(self, 'verbose', True)
|
1928
|
+
)
|
1929
|
+
|
1930
|
+
prompt = f"""Analyze these carefully selected repository files for implementing: {goal}
|
1931
|
+
|
1932
|
+
REPOSITORY: {github_url}
|
1933
|
+
GOAL: {goal}
|
1934
|
+
SELECTED FILES: {len(selected_files)}
|
1935
|
+
|
1936
|
+
FILES ANALYZED:
|
1937
|
+
{', '.join(selected_files[:20])}{'...' if len(selected_files) > 20 else ''}
|
1938
|
+
|
1939
|
+
FOCUSED CONTENT:
|
1940
|
+
{focused_content[:30000] if focused_content else "No content available"}
|
1941
|
+
|
1942
|
+
Provide comprehensive analysis focused specifically on implementing the goal: {goal}
|
1943
|
+
Since these files were pre-selected for relevance, explain how each contributes to the goal."""
|
1944
|
+
|
1945
|
+
print(" 🔍 Focused Analyzer processing selected files...")
|
1946
|
+
analysis_response = self._chat_with_agent_and_save(focused_analyzer, prompt, "FOCUSED_FILE_ANALYSIS")
|
1947
|
+
|
1948
|
+
# Generate goal-focused PRP
|
1949
|
+
return self._generate_goal_focused_prp(github_url, goal, analysis_response, selected_files)
|
1950
|
+
|
1951
|
+
def _run_gitingest_for_github(self, github_url: str) -> str:
|
1952
|
+
"""Run gitingest analysis specifically for GitHub repositories."""
|
1953
|
+
try:
|
1954
|
+
# Try using gitingest Python package first
|
1955
|
+
try:
|
1956
|
+
from gitingest import ingest
|
1957
|
+
print(f" 🔧 Using gitingest Python package for GitHub repository...")
|
1958
|
+
summary, tree, content = ingest(github_url)
|
1959
|
+
return f"SUMMARY:\n{summary}\n\nTREE:\n{tree}\n\nCONTENT:\n{content}"
|
1960
|
+
except ImportError:
|
1961
|
+
print(f" ⚠️ gitingest Python package not available, trying command line...")
|
1962
|
+
# Fallback to command line gitingest
|
1963
|
+
result = subprocess.run(
|
1964
|
+
["gitingest", github_url, "--output", "-"],
|
1965
|
+
capture_output=True,
|
1966
|
+
text=True,
|
1967
|
+
timeout=120
|
1968
|
+
)
|
1969
|
+
|
1970
|
+
if result.returncode == 0:
|
1971
|
+
return result.stdout
|
1972
|
+
else:
|
1973
|
+
print(f" ⚠️ Gitingest command failed: {result.stderr}")
|
1974
|
+
return None
|
1975
|
+
|
1976
|
+
except subprocess.TimeoutExpired:
|
1977
|
+
print(" ⚠️ Gitingest analysis timed out")
|
1978
|
+
return None
|
1979
|
+
except FileNotFoundError:
|
1980
|
+
print(" ⚠️ Gitingest not found")
|
1981
|
+
return None
|
1982
|
+
except Exception as e:
|
1983
|
+
print(f" ⚠️ Gitingest error: {e}")
|
1984
|
+
return None
|
1985
|
+
|
1986
|
+
def _get_focused_content(self, github_url: str, selected_files: List[str]) -> str:
|
1987
|
+
"""Get content for only the selected files."""
|
1988
|
+
try:
|
1989
|
+
# For now, fallback to full analysis since gitingest file filtering might not be available
|
1990
|
+
print(f" 📋 Getting focused content for {len(selected_files)} selected files...")
|
1991
|
+
return self._run_gitingest_for_github(github_url)
|
1992
|
+
|
1993
|
+
except Exception as e:
|
1994
|
+
print(f" ⚠️ Focused content extraction failed: {e}")
|
1995
|
+
return self._run_gitingest_for_github(github_url)
|
1996
|
+
|
1997
|
+
def _generate_goal_focused_prp(self, codebase: str, goal: str, analysis_response: str, selected_files: Union[List[str], List[Dict]] = None) -> str:
|
1998
|
+
"""Generate a comprehensive PRP focused on the specific goal."""
|
1999
|
+
print(f" 📝 Creating Goal-Focused PRP Generator...")
|
2000
|
+
|
2001
|
+
prp_generator = Agent(
|
2002
|
+
name="Goal-Focused PRP Generator",
|
2003
|
+
role="Expert Goal-Focused Product Requirements Prompt Specialist",
|
2004
|
+
goal=f"Generate comprehensive PRP for implementing: {goal}",
|
2005
|
+
instructions=f"""You are generating a comprehensive PRP specifically for this goal: {goal}
|
2006
|
+
|
2007
|
+
Create a PRP following PRD template methodology that includes:
|
2008
|
+
|
2009
|
+
## Purpose
|
2010
|
+
Clear statement for implementing: {goal}
|
2011
|
+
|
2012
|
+
## Goal-Specific Context
|
2013
|
+
All context needed specifically for: {goal}
|
2014
|
+
|
2015
|
+
## Implementation Blueprint for {goal}
|
2016
|
+
- Specific files to modify for {goal}
|
2017
|
+
- New files needed for {goal}
|
2018
|
+
- Dependencies required for {goal}
|
2019
|
+
- Configuration changes for {goal}
|
2020
|
+
|
2021
|
+
## Goal-Focused Validation
|
2022
|
+
- Tests specific to {goal}
|
2023
|
+
- Quality gates for {goal}
|
2024
|
+
- Success criteria for {goal}
|
2025
|
+
|
2026
|
+
Focus everything on successfully implementing: {goal}""",
|
2027
|
+
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
2028
|
+
verbose=getattr(self, 'verbose', True)
|
2029
|
+
)
|
2030
|
+
|
2031
|
+
chunk_info = ""
|
2032
|
+
if selected_files:
|
2033
|
+
chunk_info = f"\n\nSELECTED FILES ANALYZED:\n{len(selected_files)} files pre-selected for relevance"
|
2034
|
+
|
2035
|
+
prompt = f"""Generate a comprehensive Goal-Focused PRP:
|
2036
|
+
|
2037
|
+
CODEBASE: {codebase}
|
2038
|
+
GOAL: {goal}
|
2039
|
+
|
2040
|
+
REPOSITORY ANALYSIS:
|
2041
|
+
{analysis_response}
|
2042
|
+
{chunk_info}
|
2043
|
+
|
2044
|
+
Create a complete PRP following PRD template methodology, focused specifically on implementing: {goal}
|
2045
|
+
Include all necessary context, implementation guidance, and validation for this specific goal."""
|
2046
|
+
|
2047
|
+
print(" 📝 Generating goal-focused PRP...")
|
2048
|
+
prp_response = self._chat_with_agent_and_save(prp_generator, prompt, "GOAL_FOCUSED_PRP_GENERATION")
|
2049
|
+
|
2050
|
+
# Create final combined response
|
2051
|
+
all_responses = []
|
2052
|
+
|
2053
|
+
# Add parsing response
|
2054
|
+
all_responses.append({
|
2055
|
+
"agent": "Structured Input Parser",
|
2056
|
+
"phase": "Input Analysis",
|
2057
|
+
"response": f"Extracted Codebase: {codebase}\nExtracted Goal: {goal}"
|
2058
|
+
})
|
2059
|
+
|
2060
|
+
# Add repository analysis
|
2061
|
+
all_responses.append({
|
2062
|
+
"agent": "Goal-Focused Repository Analyst",
|
2063
|
+
"phase": "Repository Analysis",
|
2064
|
+
"response": analysis_response
|
2065
|
+
})
|
2066
|
+
|
2067
|
+
# Add chunk analyses if any
|
2068
|
+
if selected_files:
|
2069
|
+
for file_path in selected_files:
|
2070
|
+
all_responses.append({
|
2071
|
+
"agent": "Intelligent File Selector",
|
2072
|
+
"phase": "File Selection",
|
2073
|
+
"response": f"Selected file: {file_path}"
|
2074
|
+
})
|
2075
|
+
|
2076
|
+
# Add final PRP
|
2077
|
+
all_responses.append({
|
2078
|
+
"agent": "Goal-Focused PRP Generator",
|
2079
|
+
"phase": "Goal-Focused PRP Generation",
|
2080
|
+
"response": prp_response
|
2081
|
+
})
|
2082
|
+
|
2083
|
+
# Create combined response focused on the goal
|
2084
|
+
return self._create_goal_focused_combined_response(all_responses, codebase, goal)
|
2085
|
+
|
2086
|
+
def _generate_goal_focused_analysis(self, codebase: str, goal: str) -> str:
|
2087
|
+
"""Generate goal-focused analysis for local codebase."""
|
2088
|
+
# If we have existing agent interactions from auto_analyze, incorporate them
|
2089
|
+
if hasattr(self, 'agent_interactions') and self.agent_interactions:
|
2090
|
+
return self._create_goal_focused_from_existing(codebase, goal)
|
2091
|
+
else:
|
2092
|
+
# Perform fresh analysis focused on the goal
|
2093
|
+
return self._perform_fresh_goal_analysis(codebase, goal)
|
2094
|
+
|
2095
|
+
def _create_goal_focused_combined_response(self, agent_responses: List[Dict], codebase: str, goal: str) -> str:
|
2096
|
+
"""Create a comprehensive PRP focused on the specific goal."""
|
2097
|
+
|
2098
|
+
# Create comprehensive PRP content structured for LLM implementation
|
2099
|
+
prp_content = f"""# Product Requirements Prompt (PRP): {goal}
|
2100
|
+
|
2101
|
+
## 📋 Implementation Overview
|
2102
|
+
**Target Codebase:** {codebase}
|
2103
|
+
**Implementation Goal:** {goal}
|
2104
|
+
**Analysis Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
2105
|
+
|
2106
|
+
## 🎯 Executive Summary
|
2107
|
+
This comprehensive Product Requirements Prompt (PRP) provides all necessary context and implementation guidance for successfully implementing **{goal}** in the specified codebase. The analysis follows Context Engineering principles to ensure first-try implementation success.
|
2108
|
+
|
2109
|
+
## 📊 Repository Analysis
|
2110
|
+
"""
|
2111
|
+
|
2112
|
+
# Extract key information from agent responses
|
2113
|
+
repository_analysis = ""
|
2114
|
+
file_selections = ""
|
2115
|
+
implementation_guidance = ""
|
2116
|
+
|
2117
|
+
for response_data in agent_responses:
|
2118
|
+
agent_name = response_data.get("agent", "")
|
2119
|
+
phase = response_data.get("phase", "")
|
2120
|
+
response = response_data.get("response", "")
|
2121
|
+
|
2122
|
+
if "Repository" in phase or "Structure" in phase:
|
2123
|
+
repository_analysis += f"### {phase}\n{response}\n\n"
|
2124
|
+
elif "File Selection" in phase or "Enhanced File" in phase:
|
2125
|
+
file_selections += f"### {phase}\n{response}\n\n"
|
2126
|
+
elif "PRP Generation" in phase or "Analysis" in phase:
|
2127
|
+
implementation_guidance += f"### {phase}\n{response}\n\n"
|
2128
|
+
else:
|
2129
|
+
implementation_guidance += f"### {agent_name} - {phase}\n{response}\n\n"
|
2130
|
+
|
2131
|
+
# Build structured PRP
|
2132
|
+
prp_content += repository_analysis
|
2133
|
+
|
2134
|
+
if file_selections:
|
2135
|
+
prp_content += f"""## 📁 File Selection Strategy
|
2136
|
+
{file_selections}"""
|
2137
|
+
|
2138
|
+
prp_content += f"""## 🚀 Implementation Guidance
|
2139
|
+
{implementation_guidance}
|
2140
|
+
|
2141
|
+
## ✅ Implementation Checklist
|
2142
|
+
Based on the analysis above, follow these steps to implement **{goal}**:
|
2143
|
+
|
2144
|
+
1. **Review Selected Files**: Focus on the files identified in the analysis as most relevant to {goal}
|
2145
|
+
2. **Follow Implementation Patterns**: Use the patterns and structures identified in the repository analysis
|
2146
|
+
3. **Implement Core Functionality**: Based on the detailed guidance provided above
|
2147
|
+
4. **Test Implementation**: Ensure the implementation works with existing codebase patterns
|
2148
|
+
5. **Validate Goal Achievement**: Confirm that {goal} has been successfully implemented
|
2149
|
+
|
2150
|
+
## 🎯 Success Criteria
|
2151
|
+
- [ ] {goal} functionality is fully implemented
|
2152
|
+
- [ ] Implementation follows existing codebase patterns and conventions
|
2153
|
+
- [ ] All identified files are properly updated
|
2154
|
+
- [ ] Implementation is tested and validated
|
2155
|
+
- [ ] Goal requirements are completely satisfied
|
2156
|
+
|
2157
|
+
## 📚 Context Engineering Benefits
|
2158
|
+
This PRP provides:
|
2159
|
+
- 🎯 **Goal-Focused Analysis**: Every insight directed toward implementing {goal}
|
2160
|
+
- 🔧 **Repository Intelligence**: Deep understanding of codebase structure and patterns
|
2161
|
+
- 📋 **Actionable Guidance**: Step-by-step implementation instructions
|
2162
|
+
- ✅ **Quality Assurance**: Built-in validation and success criteria
|
2163
|
+
|
2164
|
+
**Result:** Complete implementation-ready PRP for {goal} in {codebase}
|
2165
|
+
"""
|
2166
|
+
|
2167
|
+
# Save output based on debug mode
|
2168
|
+
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
2169
|
+
safe_goal = goal.replace(' ', '_').replace('/', '_')[:50]
|
2170
|
+
|
2171
|
+
if self.debug_mode:
|
2172
|
+
# Debug mode: Save comprehensive report + individual files (already saved)
|
2173
|
+
self.save_comprehensive_session_report()
|
2174
|
+
final_output_file = self.save_markdown_output(
|
2175
|
+
content=prp_content,
|
2176
|
+
filename=f"comprehensive_prp_{safe_goal}_{timestamp}",
|
2177
|
+
section_title=f"Comprehensive PRP: {goal}"
|
2178
|
+
)
|
2179
|
+
print(f"🐛 Debug mode: Comprehensive session report + individual files + final PRP saved")
|
2180
|
+
else:
|
2181
|
+
# Non-debug mode: Save ONLY the final comprehensive PRP directly in .praison/prp
|
2182
|
+
# Use clean filename: goal name + timestamp
|
2183
|
+
final_output_file = self.output_dir / f"{safe_goal}_prp_{timestamp}.md"
|
2184
|
+
final_output_file.parent.mkdir(parents=True, exist_ok=True)
|
2185
|
+
|
2186
|
+
# Add markdown header for standalone file
|
2187
|
+
standalone_prp = f"""# Implementation PRP: {goal}
|
2188
|
+
|
2189
|
+
**Generated by ContextAgent**
|
2190
|
+
**Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
2191
|
+
**Codebase:** {codebase}
|
2192
|
+
|
2193
|
+
---
|
2194
|
+
|
2195
|
+
{prp_content}
|
2196
|
+
|
2197
|
+
---
|
2198
|
+
*This PRP contains all necessary context for implementing {goal}. Use this as your complete implementation guide.*
|
2199
|
+
"""
|
2200
|
+
|
2201
|
+
with open(final_output_file, "w", encoding="utf-8") as f:
|
2202
|
+
f.write(standalone_prp)
|
2203
|
+
|
2204
|
+
print(f"✅ Implementation PRP complete with {len(self.agent_interactions)} agent interactions")
|
2205
|
+
print(f"📝 Final PRP saved: {final_output_file}")
|
2206
|
+
if not self.debug_mode:
|
2207
|
+
print(f"📋 Single file mode: Only comprehensive PRP saved (use LOGLEVEL=debug for detailed logs)")
|
2208
|
+
|
2209
|
+
# Enhanced debug summary
|
2210
|
+
self.log_debug("Session completed",
|
2211
|
+
total_interactions=len(self.agent_interactions),
|
2212
|
+
final_output=str(final_output_file),
|
2213
|
+
goal=goal,
|
2214
|
+
codebase=codebase,
|
2215
|
+
debug_mode=self.debug_mode)
|
2216
|
+
|
2217
|
+
return prp_content
|
2218
|
+
|
2219
|
+
def _analyze_url_with_goal(self, url: str, goal: str) -> str:
|
2220
|
+
"""Analyze general URL with specific goal focus."""
|
2221
|
+
return self.generate_feature_prp(f"Analyze {url} and implement {goal}")
|
2222
|
+
|
2223
|
+
def _create_goal_focused_from_existing(self, codebase: str, goal: str) -> str:
|
2224
|
+
"""Create goal-focused analysis from existing agent interactions."""
|
2225
|
+
# Filter and refocus existing interactions on the specific goal
|
2226
|
+
goal_focused_prp = self.generate_comprehensive_prp(
|
2227
|
+
f"Implement {goal} in {codebase}",
|
2228
|
+
{
|
2229
|
+
"codebase_analysis": self.codebase_analysis,
|
2230
|
+
"pattern_library": self.pattern_library,
|
2231
|
+
"integration_points": getattr(self, 'integration_points', {}),
|
2232
|
+
"validation_framework": self.validation_framework,
|
2233
|
+
"context_documentation": self.context_documentation,
|
2234
|
+
"specific_goal": goal
|
2235
|
+
}
|
2236
|
+
)
|
2237
|
+
|
2238
|
+
return self._create_goal_focused_combined_response([
|
2239
|
+
{
|
2240
|
+
"agent": "Goal-Focused Context Refiner",
|
2241
|
+
"phase": "Goal-Focused Analysis",
|
2242
|
+
"response": goal_focused_prp
|
2243
|
+
}
|
2244
|
+
], codebase, goal)
|
2245
|
+
|
2246
|
+
def _perform_fresh_goal_analysis(self, codebase: str, goal: str) -> str:
|
2247
|
+
"""Perform fresh analysis focused on the specific goal."""
|
2248
|
+
# Trigger analysis if not already done
|
2249
|
+
if not hasattr(self, 'codebase_analysis') or not self.codebase_analysis:
|
2250
|
+
self._perform_context_engineering_analysis()
|
2251
|
+
|
2252
|
+
return self._generate_goal_focused_analysis(codebase, goal)
|
2253
|
+
|
2254
|
+
def get_agent_interaction_summary(self) -> Dict[str, Any]:
|
2255
|
+
"""Get summary of all agent interactions."""
|
2256
|
+
return {
|
2257
|
+
"total_interactions": len(self.agent_interactions),
|
2258
|
+
"phases_completed": list(set([interaction.get("phase", "") for interaction in self.agent_interactions])),
|
2259
|
+
"agents_used": list(set([interaction.get("agent_name", "") for interaction in self.agent_interactions])),
|
2260
|
+
"output_directory": self.output_dir,
|
2261
|
+
"interactions_saved": True
|
2262
|
+
}
|
2263
|
+
|
2264
|
+
def _get_structure_fallback(self, github_url: str) -> Dict[str, Any]:
|
2265
|
+
"""Fallback method to get repository structure using GitHub API."""
|
2266
|
+
try:
|
2267
|
+
# Extract repo info from URL
|
2268
|
+
repo_parts = github_url.replace('https://github.com/', '').strip('/')
|
2269
|
+
|
2270
|
+
# Try using GitHub API for tree structure
|
2271
|
+
api_url = f"https://api.github.com/repos/{repo_parts}/git/trees/main?recursive=1"
|
2272
|
+
|
2273
|
+
import urllib.request
|
2274
|
+
import json
|
2275
|
+
|
2276
|
+
with urllib.request.urlopen(api_url) as response:
|
2277
|
+
data = json.loads(response.read())
|
2278
|
+
|
2279
|
+
file_structure = []
|
2280
|
+
for item in data.get('tree', []):
|
2281
|
+
if item['type'] == 'blob': # Files only
|
2282
|
+
file_structure.append(item['path'])
|
2283
|
+
|
2284
|
+
return {
|
2285
|
+
"method": "github_api",
|
2286
|
+
"structure": '\n'.join(file_structure),
|
2287
|
+
"file_count": len(file_structure),
|
2288
|
+
"success": True
|
2289
|
+
}
|
2290
|
+
|
2291
|
+
except Exception as e:
|
2292
|
+
print(f" ⚠️ GitHub API fallback failed: {e}")
|
2293
|
+
return {
|
2294
|
+
"method": "failed",
|
2295
|
+
"structure": "Unable to retrieve repository structure",
|
2296
|
+
"file_count": 0,
|
2297
|
+
"success": False
|
2298
|
+
}
|
2299
|
+
|
2300
|
+
|
2301
|
+
def create_context_agent(llm: Optional[Union[str, Any]] = None, **kwargs) -> ContextAgent:
|
2302
|
+
"""
|
2303
|
+
Factory function to create a ContextAgent following Context Engineering and PRD methodology.
|
2304
|
+
|
2305
|
+
Args:
|
2306
|
+
llm: Language model to use (e.g., "gpt-4o-mini", "claude-3-haiku")
|
2307
|
+
**kwargs: Additional arguments to pass to ContextAgent constructor
|
2308
|
+
|
2309
|
+
Returns:
|
2310
|
+
ContextAgent: Configured ContextAgent for comprehensive context generation following PRD principles
|
2311
|
+
"""
|
2312
|
+
if llm is None:
|
2313
|
+
llm = "gpt-4o-mini"
|
2314
|
+
|
2315
|
+
return ContextAgent(llm=llm, **kwargs)
|