adr-suggestion-tool.ts•47 kB
/**
* MCP Tool for ADR suggestions and implicit decision detection
* Enhanced with Knowledge Generation, Reflexion capabilities, and Research-Driven Architecture
* Implements AI-powered ADR recommendation system with environment-aware research
*/
import { McpAdrError } from '../types/index.js';
import { ConversationContext } from '../types/conversation-context.js';
import { generateArchitecturalKnowledge } from '../utils/knowledge-generation.js';
import {
executeWithReflexion,
retrieveRelevantMemories,
createToolReflexionConfig,
} from '../utils/reflexion.js';
import { executeADRSuggestionPrompt, formatMCPResponse } from '../utils/prompt-execution.js';
import { TreeSitterAnalyzer } from '../utils/tree-sitter-analyzer.js';
import { findRelatedCode } from '../utils/file-system.js';
import { ResearchOrchestrator } from '../utils/research-orchestrator.js';
import { getAIExecutor } from '../utils/ai-executor.js';
/**
* Suggest ADRs based on project analysis with advanced prompting techniques
* Enhanced with Knowledge Generation and Reflexion learning capabilities
*/
export async function suggestAdrs(args: {
projectPath?: string;
analysisType?: 'implicit_decisions' | 'code_changes' | 'comprehensive';
beforeCode?: string;
afterCode?: string;
changeDescription?: string;
commitMessages?: string[];
existingAdrs?: string[];
enhancedMode?: boolean; // Enable advanced prompting features
learningEnabled?: boolean; // Enable Reflexion learning
knowledgeEnhancement?: boolean; // Enable Knowledge Generation
enableTreeSitterAnalysis?: boolean; // Enable tree-sitter for enhanced code analysis
conversationContext?: ConversationContext; // Context from calling LLM
}): Promise<any> {
const {
projectPath = process.cwd(),
analysisType = 'comprehensive',
beforeCode,
afterCode,
changeDescription,
commitMessages,
existingAdrs,
enhancedMode = true, // Default to enhanced mode
learningEnabled = true, // Default to learning enabled
knowledgeEnhancement = true, // Default to knowledge enhancement
enableTreeSitterAnalysis = true, // Default to tree-sitter enabled
conversationContext, // Context from calling LLM
} = args;
try {
const { analyzeImplicitDecisions, analyzeCodeChanges } = await import(
'../utils/adr-suggestions.js'
);
switch (analysisType) {
case 'implicit_decisions': {
let enhancedPrompt = '';
let enhancementInfo = '';
let codeContext = '';
// Smart Code Linking for implicit decisions
if (existingAdrs && existingAdrs.length > 0) {
try {
const mockAdrContent = existingAdrs.join('\n\n');
const relatedCodeResult = await findRelatedCode(
'implicit-decisions-analysis',
mockAdrContent,
projectPath,
{
useAI: true,
useRipgrep: true,
maxFiles: 10,
includeContent: false,
}
);
if (relatedCodeResult.relatedFiles.length > 0) {
codeContext = `\n## Smart Code Linking - Implicit Decision Context\n\nAnalyzing related code for implicit architectural decisions:\n\n${relatedCodeResult.relatedFiles
.slice(0, 5)
.map((file, index) => `${index + 1}. **${file.path}** - ${file.extension} file`)
.join(
'\n'
)}\n\n**Code Analysis**: Found ${relatedCodeResult.relatedFiles.length} related files that may contain implicit decisions\n**Keywords Used**: ${relatedCodeResult.keywords.join(', ')}\n**Confidence**: ${(relatedCodeResult.confidence * 100).toFixed(1)}%\n`;
}
} catch (error) {
console.warn('[WARNING] Smart Code Linking for implicit decisions failed:', error);
}
}
// Apply enhancements if enabled
if (enhancedMode && (knowledgeEnhancement || learningEnabled)) {
let knowledgeContext = '';
// Generate domain knowledge for implicit decision detection
if (knowledgeEnhancement) {
try {
const knowledgeResult = await generateArchitecturalKnowledge(
{
projectPath,
technologies: [],
patterns: [],
projectType: 'implicit-decision-detection',
},
{
domains: ['api-design', 'database-design'],
depth: 'basic',
cacheEnabled: true,
}
);
knowledgeContext = `\n## Knowledge Enhancement\n${knowledgeResult.prompt}\n`;
} catch (error) {
console.error('[WARNING] Knowledge generation failed:', error);
}
}
// Apply learning if enabled
if (learningEnabled) {
try {
const reflexionConfig = createToolReflexionConfig('suggest_adrs');
const baseResult = await analyzeImplicitDecisions(
projectPath,
existingAdrs,
conversationContext
);
const reflexionResult = await executeWithReflexion(
{
prompt: baseResult.analysisPrompt + knowledgeContext,
instructions: baseResult.instructions,
context: { projectPath, analysisType: 'implicit_decisions', existingAdrs },
},
reflexionConfig
);
enhancedPrompt = reflexionResult.prompt;
enhancementInfo = `
## Enhancement Status
- **Knowledge Generation**: ${knowledgeEnhancement ? '✅ Applied' : '❌ Disabled'}
- **Reflexion Learning**: ✅ Applied
- **Smart Code Linking**: ${codeContext ? '✅ Applied' : '❌ No existing ADRs'}
- **Learning from**: Past implicit decision detection tasks
`;
} catch (error) {
console.error('[WARNING] Reflexion enhancement failed:', error);
const result = await analyzeImplicitDecisions(
projectPath,
existingAdrs,
conversationContext
);
enhancedPrompt = result.analysisPrompt + knowledgeContext;
}
} else {
const result = await analyzeImplicitDecisions(
projectPath,
existingAdrs,
conversationContext
);
enhancedPrompt = result.analysisPrompt + knowledgeContext;
enhancementInfo = `
## Enhancement Status
- **Knowledge Generation**: ${knowledgeEnhancement ? '✅ Applied' : '❌ Disabled'}
- **Reflexion Learning**: ❌ Disabled
- **Smart Code Linking**: ${codeContext ? '✅ Applied' : '❌ No existing ADRs'}
`;
}
} else {
const result = await analyzeImplicitDecisions(
projectPath,
existingAdrs,
conversationContext
);
enhancedPrompt = result.analysisPrompt;
enhancementInfo = `
## Enhancement Status
- **Enhanced Mode**: ❌ Disabled
- **Smart Code Linking**: ${codeContext ? '✅ Applied' : '❌ No existing ADRs'}
- All advanced features are disabled for this analysis
`;
}
const baseResult = await analyzeImplicitDecisions(
projectPath,
existingAdrs,
conversationContext
);
return {
content: [
{
type: 'text',
text: `# ADR Suggestions: Enhanced Implicit Decisions Analysis
${enhancementInfo}
${codeContext}
${baseResult.instructions}
## Enhanced AI Analysis Prompt
${enhancedPrompt}
## Next Steps
1. **Submit the enhanced prompt** to an AI agent for comprehensive analysis
2. **Review the detected decisions** and prioritize based on impact and risk
3. **Use the \`generate_adr_from_decision\` tool** to create ADRs for high-priority decisions
4. **Integrate with existing ADR workflow** for review and approval
## Expected Output
The enhanced AI analysis will identify implicit architectural decisions and provide:
- Detailed decision analysis with evidence and domain knowledge
- Priority and risk assessments informed by past experiences
- Suggested ADR titles and content with improved quality
- Recommendations for documentation strategy based on learning
`,
},
],
};
}
case 'code_changes': {
if (!beforeCode || !afterCode || !changeDescription) {
throw new McpAdrError(
'Code change analysis requires beforeCode, afterCode, and changeDescription',
'INVALID_INPUT'
);
}
let enhancedPrompt = '';
let enhancementInfo = '';
let codeContext = '';
// Smart Code Linking for code changes
if (existingAdrs && existingAdrs.length > 0) {
try {
const mockAdrContent = `${existingAdrs.join('\n\n')}\n\nCODE CHANGE:\n${changeDescription}`;
const relatedCodeResult = await findRelatedCode(
'code-changes-analysis',
mockAdrContent,
projectPath || process.cwd(),
{
useAI: true,
useRipgrep: true,
maxFiles: 8,
includeContent: false,
}
);
if (relatedCodeResult.relatedFiles.length > 0) {
codeContext = `\n## Smart Code Linking - Code Change Context\n\nFound related code files that may be affected by this change:\n\n${relatedCodeResult.relatedFiles
.slice(0, 3)
.map(
(file, index) =>
`${index + 1}. **${file.path}** - ${file.extension} file (${file.size} bytes)`
)
.join(
'\n'
)}\n\n**Impact Analysis**: ${relatedCodeResult.relatedFiles.length} files may be affected by this architectural change\n**Keywords Used**: ${relatedCodeResult.keywords.join(', ')}\n**Confidence**: ${(relatedCodeResult.confidence * 100).toFixed(1)}%\n`;
}
} catch (error) {
console.warn('[WARNING] Smart Code Linking for code changes failed:', error);
}
}
// Apply enhancements if enabled
let treeSitterAnalysis = '';
if (enhancedMode && (knowledgeEnhancement || learningEnabled)) {
let knowledgeContext = '';
// Generate domain knowledge for code change analysis
if (knowledgeEnhancement) {
try {
const knowledgeResult = await generateArchitecturalKnowledge(
{
projectPath: projectPath || process.cwd(),
technologies: [],
patterns: [],
projectType: 'code-change-analysis',
},
{
domains: ['api-design', 'performance-optimization'],
depth: 'basic',
cacheEnabled: true,
}
);
knowledgeContext = `\n## Knowledge Enhancement\n${knowledgeResult.prompt}\n`;
} catch (error) {
console.error('[WARNING] Knowledge generation failed:', error);
}
}
// Perform tree-sitter analysis for enhanced code change understanding
if (enableTreeSitterAnalysis) {
try {
const codeChangeAnalysis = await performTreeSitterCodeChangeAnalysis(
beforeCode,
afterCode,
changeDescription
);
if (
codeChangeAnalysis.architecturalChanges.length > 0 ||
codeChangeAnalysis.securityImpacts.length > 0
) {
treeSitterAnalysis = `
## 🔍 Tree-sitter Code Change Analysis
**Analysis Results:**
- **Architectural Changes**: ${codeChangeAnalysis.architecturalChanges.length}
- **Security Impacts**: ${codeChangeAnalysis.securityImpacts.length}
- **Complexity Change**: ${codeChangeAnalysis.complexityDelta > 0 ? '+' : ''}${codeChangeAnalysis.complexityDelta}
- **New Dependencies**: ${codeChangeAnalysis.newDependencies.length}
${
codeChangeAnalysis.architecturalChanges.length > 0
? `
### 🏗️ Architectural Changes Detected
${codeChangeAnalysis.architecturalChanges.map(change => `- **${change.type}**: ${change.description} (impact: ${change.impact})`).join('\n')}
`
: ''
}
${
codeChangeAnalysis.securityImpacts.length > 0
? `
### 🔒 Security Impact Analysis
${codeChangeAnalysis.securityImpacts.map(impact => `- **${impact.type}**: ${impact.description} (severity: ${impact.severity})`).join('\n')}
`
: ''
}
${
codeChangeAnalysis.newDependencies.length > 0
? `
### 📦 New Dependencies
${codeChangeAnalysis.newDependencies.map(dep => `- **${dep.name}**: ${dep.reason} (risk: ${dep.riskLevel})`).join('\n')}
`
: ''
}
---
`;
}
} catch (error) {
console.warn('Tree-sitter code change analysis failed:', error);
treeSitterAnalysis = `
## 🔍 Tree-sitter Code Change Analysis
**Status**: ⚠️ Analysis failed - continuing with standard analysis
**Error**: ${error instanceof Error ? error.message : 'Unknown error'}
---
`;
}
}
// Apply learning if enabled
if (learningEnabled) {
try {
const reflexionConfig = createToolReflexionConfig('suggest_adrs', {
evaluationCriteria: ['task-success', 'accuracy', 'relevance'],
});
const baseResult = await analyzeCodeChanges(
beforeCode,
afterCode,
changeDescription,
commitMessages
);
const reflexionResult = await executeWithReflexion(
{
prompt: baseResult.analysisPrompt + knowledgeContext,
instructions: baseResult.instructions,
context: {
analysisType: 'code_changes',
changeDescription,
hasCommitMessages: !!commitMessages?.length,
},
},
reflexionConfig
);
enhancedPrompt = reflexionResult.prompt;
enhancementInfo = `
## Enhancement Status
- **Knowledge Generation**: ${knowledgeEnhancement ? '✅ Applied' : '❌ Disabled'}
- **Reflexion Learning**: ✅ Applied
- **Smart Code Linking**: ${codeContext ? '✅ Applied' : '❌ No existing ADRs'}
- **Learning from**: Past code change analysis tasks
`;
} catch (error) {
console.error('[WARNING] Reflexion enhancement failed:', error);
const result = await analyzeCodeChanges(
beforeCode,
afterCode,
changeDescription,
commitMessages
);
enhancedPrompt = result.analysisPrompt + knowledgeContext;
}
} else {
const result = await analyzeCodeChanges(
beforeCode,
afterCode,
changeDescription,
commitMessages
);
enhancedPrompt = result.analysisPrompt + knowledgeContext;
enhancementInfo = `
## Enhancement Status
- **Knowledge Generation**: ${knowledgeEnhancement ? '✅ Applied' : '❌ Disabled'}
- **Reflexion Learning**: ❌ Disabled
- **Smart Code Linking**: ${codeContext ? '✅ Applied' : '❌ No existing ADRs'}
`;
}
} else {
const result = await analyzeCodeChanges(
beforeCode,
afterCode,
changeDescription,
commitMessages
);
enhancedPrompt = result.analysisPrompt;
enhancementInfo = `
## Enhancement Status
- **Enhanced Mode**: ❌ Disabled
- **Smart Code Linking**: ${codeContext ? '✅ Applied' : '❌ No existing ADRs'}
- All advanced features are disabled for this analysis
`;
}
const baseResult = await analyzeCodeChanges(
beforeCode,
afterCode,
changeDescription,
commitMessages
);
return {
content: [
{
type: 'text',
text: `# ADR Suggestions: Enhanced Code Change Analysis
${enhancementInfo}
${codeContext}
${treeSitterAnalysis}
${baseResult.instructions}
## Enhanced AI Analysis Prompt
${enhancedPrompt}
## Next Steps
1. **Submit the enhanced prompt** to an AI agent for change analysis
2. **Review the identified decisions** reflected in the code changes
3. **Document significant decisions** as ADRs using the generation tool
4. **Follow up with development team** for any clarification questions
## Expected Output
The enhanced AI analysis will provide:
- Architectural decisions reflected in the changes with domain context
- Change motivation and context analysis informed by past experiences
- Impact and risk assessment with improved accuracy
- Recommendations for documentation based on learning patterns
`,
},
],
};
}
case 'comprehensive': {
let enhancedPrompt = '';
let knowledgeContext = '';
let reflexionContext = '';
let codeContext = '';
let researchContext = '';
// Step 0: Research current infrastructure state (NEW - Research-Driven Architecture)
try {
const orchestrator = new ResearchOrchestrator(projectPath, 'docs/adrs');
// Research questions about current architecture
const infrastructureResearch = await orchestrator.answerResearchQuestion(
'What is our current infrastructure and deployment architecture? Include container technology, orchestration, databases, and key architectural patterns.'
);
if (infrastructureResearch.confidence >= 0.5) {
researchContext = `
## 🔬 Research-Driven Architecture Analysis
**Live Infrastructure Research Results:**
### Current State
${infrastructureResearch.answer}
### Data Sources Consulted
${infrastructureResearch.sources.map(s => `- **${s.type}** (confidence: ${(s.confidence * 100).toFixed(1)}%)`).join('\n')}
### Research Metadata
- **Overall Confidence**: ${(infrastructureResearch.confidence * 100).toFixed(1)}%
- **Files Analyzed**: ${infrastructureResearch.metadata.filesAnalyzed}
- **Sources Queried**: ${infrastructureResearch.metadata.sourcesQueried.join(', ')}
- **Research Duration**: ${infrastructureResearch.metadata.duration}ms
${infrastructureResearch.needsWebSearch ? '- **Recommendation**: Consider web search for additional context\n' : ''}
### Infrastructure Evidence
${infrastructureResearch.sources
.filter(s => s.found && s.data)
.map(s => {
if (s.type === 'project_files') {
return `**Project Files**: ${s.data.files?.length || 0} relevant files found`;
} else if (s.type === 'environment') {
return `**Environment Data**: ${JSON.stringify(s.data).substring(0, 200)}...`;
} else if (s.type === 'knowledge_graph') {
return `**Knowledge Graph**: ${s.data.relatedAdrs?.length || 0} related ADRs`;
}
return '';
})
.filter(Boolean)
.join('\n')}
---
`;
}
} catch (error) {
console.warn('[WARNING] Research-driven infrastructure analysis failed:', error);
researchContext = `
## 🔬 Research-Driven Architecture Analysis
**Status**: ⚠️ Research analysis unavailable
**Error**: ${error instanceof Error ? error.message : 'Unknown error'}
Proceeding with traditional analysis methods...
---
`;
}
// Step 1: Generate domain-specific knowledge if enabled
if (knowledgeEnhancement) {
try {
const knowledgeResult = await generateArchitecturalKnowledge(
{
projectPath,
technologies: [], // Will be auto-detected from project
patterns: [],
projectType: 'software-architecture',
existingAdrs: existingAdrs || [],
},
{
domains: ['api-design', 'database-design', 'microservices'],
depth: 'intermediate',
cacheEnabled: true,
}
);
knowledgeContext = `
## Domain-Specific Knowledge Enhancement
The following architectural knowledge has been generated to enhance ADR suggestions:
${knowledgeResult.prompt}
---
`;
} catch (error) {
console.error('[WARNING] Knowledge generation failed:', error);
knowledgeContext = '<!-- Knowledge generation unavailable -->\n';
}
}
// Step 1a: Smart Code Linking - Analyze existing ADRs for related code
if (existingAdrs && existingAdrs.length > 0) {
try {
// For comprehensive analysis, we'll simulate ADR content analysis
// In a real scenario, this would read actual ADR files
const mockAdrContent = existingAdrs.join('\n\n');
const relatedCodeResult = await findRelatedCode(
'comprehensive-analysis',
mockAdrContent,
projectPath,
{
useAI: true,
useRipgrep: true,
maxFiles: 15,
includeContent: false,
}
);
if (relatedCodeResult.relatedFiles.length > 0) {
codeContext = `
## Smart Code Linking Analysis
Found ${relatedCodeResult.relatedFiles.length} code files related to existing ADRs:
### Related Code Files
${relatedCodeResult.relatedFiles
.map(
(file, index) => `
${index + 1}. **${file.path}** (${file.extension} file)
- Size: ${file.size} bytes
- Directory: ${file.directory}
`
)
.join('')}
### Search Information
- **Keywords Used**: ${relatedCodeResult.keywords.join(', ')}
- **Search Patterns**: ${relatedCodeResult.searchPatterns.join(', ')}
- **Confidence**: ${(relatedCodeResult.confidence * 100).toFixed(1)}%
**Analysis Summary**: ${relatedCodeResult.relatedFiles.length} related files found
---
`;
} else {
codeContext = `
## Smart Code Linking Analysis
**Status**: No related code files found for existing ADRs
**Keywords Searched**: ${relatedCodeResult.keywords.join(', ')}
**Patterns Used**: ${relatedCodeResult.searchPatterns.join(', ')}
This suggests either:
- ADRs are high-level architectural decisions not yet implemented
- Code patterns don't match ADR terminology
- Additional implementation work may be needed
---
`;
}
} catch (error) {
console.warn('[WARNING] Smart Code Linking failed:', error);
codeContext = `
## Smart Code Linking Analysis
**Status**: ⚠️ Analysis failed - continuing without code context
**Error**: ${error instanceof Error ? error.message : 'Unknown error'}
---
`;
}
}
// Step 2: Apply Reflexion learning if enabled
if (learningEnabled) {
try {
// Retrieve relevant memories from past ADR suggestion tasks
const memoryResult = await retrieveRelevantMemories(
'adr-suggestion',
{ projectPath, analysisType: 'comprehensive', existingAdrs },
{ maxResults: 5, relevanceThreshold: 0.6 }
);
reflexionContext = `
## Learning from Past Experiences
The following insights from past ADR suggestion tasks will inform this analysis:
${memoryResult.prompt}
---
`;
} catch (error) {
console.error('[WARNING] Reflexion memory retrieval failed:', error);
reflexionContext = '<!-- Learning context unavailable -->\n';
}
}
// Step 3: Get the base analysis
const implicitResult = await analyzeImplicitDecisions(
projectPath,
existingAdrs,
conversationContext
);
// Step 4: Apply Reflexion execution if learning is enabled
if (learningEnabled) {
try {
const reflexionConfig = createToolReflexionConfig('suggest_adrs', {
reflectionDepth: 'detailed',
evaluationCriteria: ['task-success', 'relevance', 'clarity'],
learningRate: 0.7,
});
const reflexionResult = await executeWithReflexion(
{
prompt: implicitResult.analysisPrompt,
instructions: implicitResult.instructions,
context: {
projectPath,
analysisType: 'comprehensive',
existingAdrs,
knowledgeEnhanced: knowledgeEnhancement,
learningEnabled: true,
},
},
reflexionConfig
);
enhancedPrompt = `
## Enhanced Analysis with Learning
${reflexionResult.prompt}
---
`;
} catch (error) {
console.error('[WARNING] Reflexion execution failed:', error);
enhancedPrompt = implicitResult.analysisPrompt;
}
} else {
enhancedPrompt = implicitResult.analysisPrompt;
}
// Execute the analysis with AI if enabled, otherwise return prompt
const aiExecutor = getAIExecutor();
let executionResult: any;
// Try AI-powered analysis with research context
if (aiExecutor.isAvailable()) {
try {
const aiResult = await aiExecutor.executeStructuredPrompt(
`You are an expert software architect analyzing a project for ADR recommendations.
${researchContext}
${knowledgeContext}
${reflexionContext}
${codeContext}
Based on the research data above, generate comprehensive ADR suggestions.
${implicitResult.instructions}
${enhancedPrompt}`,
null,
{
temperature: 0.3,
maxTokens: 4000,
systemPrompt:
'You are an architecture expert. Generate ADR recommendations based on ACTUAL infrastructure data from research, not assumptions. Reference specific files, environment data, and existing ADRs found in the research.',
}
);
executionResult = {
isAIGenerated: true,
content:
typeof aiResult.data === 'string'
? aiResult.data
: JSON.stringify(aiResult.data, null, 2),
metadata: aiResult.raw.metadata,
};
} catch (error) {
console.warn('[WARNING] AI execution failed, falling back to prompt-only mode:', error);
executionResult = await executeADRSuggestionPrompt(
enhancedPrompt,
implicitResult.instructions,
{
temperature: 0.1,
maxTokens: 4000,
}
);
}
} else {
executionResult = await executeADRSuggestionPrompt(
enhancedPrompt,
implicitResult.instructions,
{
temperature: 0.1,
maxTokens: 4000,
}
);
}
if (executionResult.isAIGenerated) {
// AI execution successful - return actual analysis results
return formatMCPResponse({
...executionResult,
content: `# ADR Suggestions: AI Analysis Results (Research-Driven)
## Enhancement Features
- **Research-Driven Analysis**: ✅ Enabled (Live infrastructure data)
- **Knowledge Generation**: ${knowledgeEnhancement ? '✅ Enabled' : '❌ Disabled'}
- **Reflexion Learning**: ${learningEnabled ? '✅ Enabled' : '❌ Disabled'}
- **Enhanced Mode**: ${enhancedMode ? '✅ Enabled' : '❌ Disabled'}
- **Smart Code Linking**: ${existingAdrs && existingAdrs.length > 0 ? '✅ Enabled' : '❌ No existing ADRs'}
- **AI Execution**: ✅ OpenRouter.ai ${executionResult.metadata?.model || 'enabled'}
## Project Analysis
- **Project Path**: ${projectPath}
- **Existing ADRs**: ${existingAdrs?.length || 0} ADRs provided
- **Analysis Type**: Comprehensive (Research + AI-driven)
- **AI Response Time**: ${executionResult.metadata?.executionTime || 'N/A'}ms
- **Tokens Used**: ${executionResult.metadata?.usage?.totalTokens || 'N/A'}
${researchContext}
${codeContext}
${knowledgeContext}
${reflexionContext}
## AI Analysis Results
${executionResult.content}
## Next Steps
Based on the analysis above:
1. **Review Suggested ADRs**: Examine each suggested architectural decision
2. **Prioritize by Impact**: Focus on high-impact decisions first
3. **Generate ADRs**: Use the \`generate_adr_from_decision\` tool for priority decisions
4. **Implement Changes**: Plan and execute the architectural changes
5. **Update Documentation**: Keep ADRs current as decisions evolve
## Integration Workflow
For each suggested decision, use:
\`\`\`json
{
"tool": "generate_adr_from_decision",
"args": {
"decisionData": {
"title": "Decision title from analysis",
"context": "Context from analysis",
"decision": "Decision description",
"consequences": "Consequences from analysis"
}
}
}
\`\`\`
`,
});
} else {
// Fallback to prompt-only mode
return {
content: [
{
type: 'text',
text: `# ADR Suggestions: Enhanced Comprehensive Analysis (Research-Driven)
This enhanced analysis uses research-driven architecture with live infrastructure data and advanced prompting techniques.
## Enhancement Features
- **Research-Driven Analysis**: ✅ Enabled (Live infrastructure data)
- **Knowledge Generation**: ${knowledgeEnhancement ? '✅ Enabled' : '❌ Disabled'}
- **Reflexion Learning**: ${learningEnabled ? '✅ Enabled' : '❌ Disabled'}
- **Enhanced Mode**: ${enhancedMode ? '✅ Enabled' : '❌ Disabled'}
- **Smart Code Linking**: ${existingAdrs && existingAdrs.length > 0 ? '✅ Enabled' : '❌ No existing ADRs'}
- **AI Execution**: ❌ Disabled (configure OPENROUTER_API_KEY for AI execution)
## Project Analysis
- **Project Path**: ${projectPath}
- **Existing ADRs**: ${existingAdrs?.length || 0} ADRs provided
- **Analysis Type**: Comprehensive (Research-driven + Enhanced prompting)
${researchContext}
${codeContext}
${knowledgeContext}
${reflexionContext}
## AI Analysis Instructions
${implicitResult.instructions}
## Enhanced AI Analysis Prompt
${enhancedPrompt}
## Recommended Workflow
### 1. **Initial Analysis**
Submit the prompt above to get comprehensive decision detection
### 2. **Priority Review**
- Focus on **high** and **critical** priority decisions first
- Consider **risk level** and **complexity** for planning
- Group related decisions using suggested clusters
### 3. **ADR Generation**
Use \`generate_adr_from_decision\` tool for each prioritized decision
### 4. **Integration**
- Save generated ADRs to your ADR directory
- Update ADR index/catalog
- Schedule team review sessions
- Plan implementation tasks
`,
},
],
};
}
}
default:
throw new McpAdrError(`Unknown analysis type: ${analysisType}`, 'INVALID_INPUT');
}
} catch (error) {
throw new McpAdrError(
`Failed to suggest ADRs: ${error instanceof Error ? error.message : String(error)}`,
'SUGGESTION_ERROR'
);
}
}
/**
* Generate ADR from decision data
*/
export async function generateAdrFromDecision(args: {
decisionData: {
title: string;
context: string;
decision: string;
consequences: string;
alternatives?: string[];
evidence?: string[];
};
templateFormat?: 'nygard' | 'madr' | 'custom';
existingAdrs?: string[];
adrDirectory?: string;
}): Promise<any> {
const {
decisionData,
templateFormat = 'nygard',
existingAdrs = [],
adrDirectory = 'docs/adrs',
} = args;
try {
const { generateAdrFromDecision, generateNextAdrNumber, suggestAdrFilename } = await import(
'../utils/adr-suggestions.js'
);
if (
!decisionData.title ||
!decisionData.context ||
!decisionData.decision ||
!decisionData.consequences
) {
throw new McpAdrError(
'Decision data must include title, context, decision, and consequences',
'INVALID_INPUT'
);
}
const result = await generateAdrFromDecision(decisionData, templateFormat, existingAdrs);
// Generate suggested metadata
const adrNumber = generateNextAdrNumber(existingAdrs);
const filename = suggestAdrFilename(decisionData.title, adrNumber);
const fullPath = `${adrDirectory}/${filename}`;
// Execute ADR generation with AI if enabled
const { executeADRGenerationPrompt } = await import('../utils/prompt-execution.js');
const executionResult = await executeADRGenerationPrompt(
result.generationPrompt,
result.instructions,
{
temperature: 0.1,
maxTokens: 6000,
responseFormat: 'text',
}
);
if (executionResult.isAIGenerated) {
// AI execution successful - return actual ADR content
return formatMCPResponse({
...executionResult,
content: `# Generated ADR: ${decisionData.title}
## ADR Metadata
- **ADR Number**: ${adrNumber}
- **Filename**: ${filename}
- **Full Path**: ${fullPath}
- **Template Format**: ${templateFormat.toUpperCase()}
## Generated ADR Content
${executionResult.content}
## File Creation Instructions
To save this ADR to your project:
1. **Create the ADR directory** (if it doesn't exist):
\`\`\`bash
mkdir -p ${adrDirectory}
\`\`\`
2. **Save the ADR content** to the file:
\`\`\`bash
cat > "${fullPath}" << 'EOF'
${executionResult.content}
EOF
\`\`\`
3. **Verify the file** was created successfully:
\`\`\`bash
ls -la "${fullPath}"
\`\`\`
## Next Steps
1. **Review the generated ADR** for accuracy and completeness
2. **Save the file** using the instructions above
3. **Update your ADR index** or catalog
4. **Share with stakeholders** for review and approval
5. **Plan implementation** of the architectural decision
## Quality Checklist
- ✅ **Title** is clear and descriptive
- ✅ **Context** explains the problem and constraints
- ✅ **Decision** is specific and actionable
- ✅ **Consequences** cover both positive and negative impacts
- ✅ **Format** follows ${templateFormat.toUpperCase()} template standards
- ✅ **Numbering** is sequential (${adrNumber})
`,
});
} else {
// Fallback to prompt-only mode
const { ensureDirectoryCompat: ensureDirectory, writeFileCompat: writeFile } = await import(
'../utils/file-system.js'
);
const ensureDirPrompt = await ensureDirectory(adrDirectory);
const writeFilePrompt = await writeFile(fullPath, '[ADR_CONTENT_PLACEHOLDER]');
return {
content: [
{
type: 'text',
text: `# ADR Generation: ${decisionData.title}
${result.instructions}
## Suggested ADR Metadata
- **ADR Number**: ${adrNumber}
- **Filename**: ${filename}
- **Full Path**: ${fullPath}
- **Template Format**: ${templateFormat.toUpperCase()}
## Step 1: Create ADR Directory
${ensureDirPrompt.prompt}
## Step 2: Generate ADR Content
${result.generationPrompt}
## Step 3: Save ADR to File
After generating the ADR content from Step 2, create the ADR file:
${writeFilePrompt.prompt}
**Important**: Replace \`[ADR_CONTENT_PLACEHOLDER]\` with the actual generated ADR content from Step 2.
`,
},
],
};
}
} catch (error) {
throw new McpAdrError(
`Failed to generate ADR: ${error instanceof Error ? error.message : String(error)}`,
'GENERATION_ERROR'
);
}
}
/**
* Discover existing ADRs in the project using internal file system tools
*
* IMPORTANT FOR AI ASSISTANTS: This tool performs TWO critical functions:
* 1. PRIMARY: Scans the specified ADR directory and catalogs all existing ADRs
* 2. SECONDARY: ALWAYS initializes the complete .mcp-adr-cache infrastructure
*
* The cache initialization happens REGARDLESS of whether ADRs are found, making
* this the recommended FIRST STEP for any project workflow. All other MCP tools
* depend on this cache infrastructure to function properly.
*
* Cache files created:
* - .mcp-adr-cache/todo-data.json (TODO management backend)
* - .mcp-adr-cache/project-health-scores.json (project health metrics)
* - .mcp-adr-cache/knowledge-graph-snapshots.json (architectural knowledge)
* - .mcp-adr-cache/todo-sync-state.json (synchronization state)
*
* Therefore, always run this tool first, even for projects without existing ADRs.
*/
export async function discoverExistingAdrs(args: {
adrDirectory?: string;
includeContent?: boolean;
projectPath?: string;
}): Promise<any> {
const { adrDirectory = 'docs/adrs', includeContent = false, projectPath = process.cwd() } = args;
try {
// INITIALIZE COMPLETE CACHE INFRASTRUCTURE (since this is typically the first command)
console.log('🚀 Initializing complete cache infrastructure...');
// 1. TodoJsonManager removed - use mcp-shrimp-task-manager for task management
console.warn(
'⚠️ TodoJsonManager is deprecated and was removed in memory-centric transformation'
);
// Skip todo initialization - TodoJsonManager removed
console.log('✅ Initialized todo-data.json and cache directory');
// 2. ProjectHealthScoring removed - use relationship-based importance instead
console.warn(
'⚠️ ProjectHealthScoring is deprecated and was removed in memory-centric transformation'
);
// Skip health scoring initialization - ProjectHealthScoring removed
console.log('✅ Initialized project-health-scores.json');
// 3. Initialize KnowledgeGraphManager (creates knowledge-graph-snapshots.json and todo-sync-state.json)
// Set PROJECT_PATH temporarily for proper initialization
const originalConfig = process.env['PROJECT_PATH'];
process.env['PROJECT_PATH'] = projectPath;
const { KnowledgeGraphManager } = await import('../utils/knowledge-graph-manager.js');
const kgManager = new KnowledgeGraphManager();
await kgManager.loadKnowledgeGraph(); // Creates knowledge-graph-snapshots.json and todo-sync-state.json
console.log('✅ Initialized knowledge-graph-snapshots.json and todo-sync-state.json');
// Restore original config
if (originalConfig !== undefined) {
process.env['PROJECT_PATH'] = originalConfig;
} else {
delete process.env['PROJECT_PATH'];
}
console.log('🎯 Complete cache infrastructure ready!');
// Use the new ADR discovery utility
const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js');
const discoveryResult = await discoverAdrsInDirectory(
adrDirectory,
includeContent,
projectPath
);
// Format the results for MCP response
return {
content: [
{
type: 'text',
text: `# 🎯 Complete ADR Discovery & Cache Infrastructure Initialized
## Cache Infrastructure Status
✅ **todo-data.json** - JSON-first TODO system initialized
✅ **project-health-scores.json** - Multi-component project health scoring
✅ **knowledge-graph-snapshots.json** - Knowledge graph system & intent tracking
✅ **todo-sync-state.json** - TODO synchronization state
✅ **Cache Directory** - Complete infrastructure ready at \`.mcp-adr-cache/\`
## ADR Discovery Results
### Discovery Summary
- **Directory**: ${discoveryResult.directory}
- **Total ADRs Found**: ${discoveryResult.totalAdrs}
- **Include Content**: ${includeContent ? 'Yes' : 'No (metadata only)'}
## Discovered ADRs
${
discoveryResult.adrs.length > 0
? discoveryResult.adrs
.map(
adr => `
### ${adr.title}
- **File**: ${adr.filename}
- **Status**: ${adr.status}
- **Date**: ${adr.date || 'Not specified'}
- **Path**: ${adr.path}
${adr.metadata?.number ? `- **Number**: ${adr.metadata.number}` : ''}
${adr.metadata?.category ? `- **Category**: ${adr.metadata.category}` : ''}
${adr.metadata?.tags?.length ? `- **Tags**: ${adr.metadata.tags.join(', ')}` : ''}
${
includeContent && adr.content
? `
#### Content Preview
\`\`\`markdown
${adr.content.slice(0, 500)}${adr.content.length > 500 ? '...' : ''}
\`\`\`
`
: ''
}
`
)
.join('\n')
: 'No ADRs found in the specified directory.'
}
## Summary Statistics
### By Status
${
Object.entries(discoveryResult.summary.byStatus)
.map(([status, count]) => `- **${status}**: ${count}`)
.join('\n') || 'No status information available'
}
### By Category
${
Object.entries(discoveryResult.summary.byCategory)
.map(([category, count]) => `- **${category}**: ${count}`)
.join('\n') || 'No category information available'
}
## Recommendations
${discoveryResult.recommendations.map(rec => `- ${rec}`).join('\n')}
## Next Steps
Based on the discovered ADRs, you can:
1. **Analyze for Missing Decisions**: Use the \`suggest_adrs\` tool with the discovered ADR titles
2. **Generate Implementation TODOs**: Use the \`generate_adr_todo\` tool
3. **Create New ADRs**: Use the \`generate_adr_from_decision\` tool for new decisions
### Example Commands
To suggest new ADRs based on discovered ones:
\`\`\`json
{
"tool": "suggest_adrs",
"args": {
"existingAdrs": ${JSON.stringify(discoveryResult.adrs.map(adr => adr.title))},
"analysisType": "comprehensive"
}
}
\`\`\`
To generate a todo list from discovered ADRs:
\`\`\`json
{
"tool": "generate_adr_todo",
"args": {
"adrDirectory": "${adrDirectory}",
"scope": "all"
}
}
\`\`\`
## Raw Discovery Data
For programmatic use, the raw discovery data is:
\`\`\`json
${JSON.stringify(discoveryResult, null, 2)}
\`\`\`
`,
},
],
};
} catch (error) {
throw new McpAdrError(
`Failed to discover ADRs: ${error instanceof Error ? error.message : String(error)}`,
'DISCOVERY_ERROR'
);
}
}
/**
* Perform tree-sitter analysis for code change architectural impact
*/
async function performTreeSitterCodeChangeAnalysis(
beforeCode: string,
afterCode: string,
_changeDescription: string
): Promise<{
architecturalChanges: Array<{
type: string;
description: string;
impact: string;
}>;
securityImpacts: Array<{
type: string;
description: string;
severity: string;
}>;
complexityDelta: number;
newDependencies: Array<{
name: string;
reason: string;
riskLevel: string;
}>;
}> {
const analyzer = new TreeSitterAnalyzer();
const { writeFileSync, unlinkSync } = await import('fs');
const { join } = await import('path');
const { tmpdir } = await import('os');
const results = {
architecturalChanges: [] as any[],
securityImpacts: [] as any[],
complexityDelta: 0,
newDependencies: [] as any[],
};
try {
// Determine file extension based on content patterns
let extension = '.txt';
if (
beforeCode.includes('import ') ||
beforeCode.includes('export ') ||
beforeCode.includes('function ')
) {
extension =
beforeCode.includes('interface ') || beforeCode.includes(': string') ? '.ts' : '.js';
} else if (beforeCode.includes('def ') || beforeCode.includes('import ')) {
extension = '.py';
}
// Create temporary files for analysis
const beforeFile = join(tmpdir(), `before-analysis-${Date.now()}${extension}`);
const afterFile = join(tmpdir(), `after-analysis-${Date.now()}${extension}`);
writeFileSync(beforeFile, beforeCode);
writeFileSync(afterFile, afterCode);
try {
// Analyze both versions
const beforeAnalysis = await analyzer.analyzeFile(beforeFile);
const afterAnalysis = await analyzer.analyzeFile(afterFile);
// Compare complexity
const beforeComplexity =
beforeAnalysis.functions?.reduce((sum, func) => sum + func.complexity, 0) || 0;
const afterComplexity =
afterAnalysis.functions?.reduce((sum, func) => sum + func.complexity, 0) || 0;
results.complexityDelta = afterComplexity - beforeComplexity;
// Detect new dependencies
const beforeImports = new Set(beforeAnalysis.imports?.map(imp => imp.module) || []);
const afterImports = afterAnalysis.imports?.map(imp => imp.module) || [];
for (const imp of afterImports) {
if (!beforeImports.has(imp)) {
const riskLevel =
imp.includes('eval') || imp.includes('exec') || imp.includes('shell')
? 'high'
: imp.includes('crypto') || imp.includes('auth')
? 'medium'
: 'low';
results.newDependencies.push({
name: imp,
reason: `New dependency introduced in code change`,
riskLevel,
});
}
}
// Detect architectural changes
const beforeFunctions = beforeAnalysis.functions?.map(f => f.name) || [];
const afterFunctions = afterAnalysis.functions?.map(f => f.name) || [];
// New functions
for (const funcName of afterFunctions) {
if (!beforeFunctions.includes(funcName)) {
const funcType = funcName.toLowerCase().includes('controller')
? 'controller'
: funcName.toLowerCase().includes('service')
? 'service'
: funcName.toLowerCase().includes('repository')
? 'repository'
: 'function';
results.architecturalChanges.push({
type: 'new_component',
description: `New ${funcType}: ${funcName}`,
impact: funcType === 'controller' ? 'high' : funcType === 'service' ? 'medium' : 'low',
});
}
}
// Security impact analysis
const beforeSecrets = beforeAnalysis.secrets?.length || 0;
const afterSecrets = afterAnalysis.secrets?.length || 0;
if (afterSecrets > beforeSecrets) {
results.securityImpacts.push({
type: 'secret_introduction',
description: `${afterSecrets - beforeSecrets} new secrets detected in code`,
severity: 'high',
});
}
// Check for security issues
if (afterAnalysis.securityIssues && afterAnalysis.securityIssues.length > 0) {
for (const issue of afterAnalysis.securityIssues) {
results.securityImpacts.push({
type: issue.type,
description: issue.message,
severity: issue.severity,
});
}
}
} finally {
// Clean up temp files
try {
unlinkSync(beforeFile);
unlinkSync(afterFile);
} catch {
// Ignore cleanup errors
}
}
} catch (error) {
console.warn('Tree-sitter code change analysis failed:', error);
}
return results;
}