codeplot
Version:
Interactive CLI tool for feature planning and ADR generation using Gemini 2.5 Pro
159 lines (138 loc) • 5.45 kB
text/typescript
import 'reflect-metadata';
import { injectable, inject } from 'tsyringe';
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { BaseMessage, HumanMessage } from '@langchain/core/messages';
import { logger } from '../utils/logger';
interface ADRResult {
adrContent: string;
adrNumber: string;
adrTitle: string;
implementationPlan: string;
}
@injectable()
export class ADRGeneratorAgent {
private model: ChatGoogleGenerativeAI;
private systemPrompt: string;
constructor(
@inject('ApiKey') apiKey: string,
@inject('ModelName') modelName: string = 'gemini-2.5-pro'
) {
this.model = new ChatGoogleGenerativeAI({
model: modelName,
apiKey,
temperature: 0.7,
streaming: true,
});
this.systemPrompt = `You are a Senior Software Architect focused on ADR CREATION ONLY.
Your SOLE responsibility is to assist the user in creating Architecture Decision Records (ADRs) based on gathered requirements and context.
## Your Process:
1. Understand the feature request and context
2. Use user requirements and context to suggest a comprehensive ADR
3. Ensure the ADR follows proper format and contains necessary details
## Response Format:
You must structure your response in markdown including these sections:
- # ADR: [Title]
- ## Status: [Proposed|Accepted|Rejected]
- ## Context
- ## Decision
- ## Status
- ## Consequences
- ## Implementation Plan
## Important Formatting Rules:
- Use exactly one # header for the ADR title
- Provide clear and concise context, decision, and consequences
- Include a feasible implementation plan with step-by-step guidance
## Critical Rules:
- NEVER create ADRs without user input
- NEVER generate code or implementation directly
- Focus only on structuring user-provided information into ADR format
- Ask questions if context or requirements are unclear
## When to Proceed:
Only begin ADR creation when you have complete understanding of:
✓ Feature behavior and requirements
✓ User interactions and edge cases
✓ Data flows and business rules
✓ Performance and security considerations
Proceeding without all necessary information risks creating an incomplete or incorrect ADR. Always seek clarification when needed.`;
}
async generateADR(
featureRequest: string,
conversationHistory: BaseMessage[],
codebaseContext: string
): Promise<ADRResult> {
logger.debug('ADRGeneratorAgent: generateADR called', {
featureRequestLength: featureRequest.length,
conversationHistoryLength: conversationHistory.length,
codebaseContextLength: codebaseContext.length,
});
const prompt = ChatPromptTemplate.fromMessages([
['system', this.systemPrompt],
new MessagesPlaceholder('history'),
]);
const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
const adrResult = await chain.invoke({
history: this.prepareHistoryForADR(featureRequest, codebaseContext, conversationHistory),
});
logger.info('ADRGeneratorAgent: ADR generated successfully', {
adrLength: adrResult.length,
});
return {
adrContent: adrResult,
adrNumber: this.getNextADRNumber(),
adrTitle: this.extractTitle(adrResult),
implementationPlan: this.extractImplementationPlan(adrResult),
};
}
private prepareHistoryForADR(
featureRequest: string,
codebaseContext: string,
history: BaseMessage[]
): BaseMessage[] {
const initialMessage = new HumanMessage(
`Feature Request: ${featureRequest}\n\nCodebase Context:\n${codebaseContext}`
);
return [initialMessage, ...history.slice(1)];
}
async generateImplementationSteps(adrContent: string, codebaseContext: string): Promise<string> {
logger.debug('ADRGeneratorAgent: generateImplementationSteps called', {
adrLength: adrContent.length,
codebaseContextLength: codebaseContext.length,
});
const prompt = ChatPromptTemplate.fromMessages([
[
'system',
'You are a precision-oriented software architect. Provide detailed implementation steps based on the following ADR content.\\n\\nResponse format:\\n- Step 1: [Detailed step description]\\n- Step 2: [Detailed step description]\\n- Include code snippets when applicable.\\n',
],
[
'human',
`ADR Content:
{adrContent}
Codebase Context:
{codebaseContext}
Generate detailed implementation steps that reference specific files and provide actionable guidance.`,
],
]);
const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
return await chain.invoke({
adrContent,
codebaseContext,
});
}
extractTitle(adrContent: string): string {
const titleMatch = adrContent.match(/# ADR:\\s*\\d*\\s*-*\\s*(.+)/);
return titleMatch ? titleMatch[1].trim() : 'Untitled ADR';
}
extractImplementationPlan(adrContent: string): string {
const planMatch = adrContent.match(/## Implementation Plan\\s*([\\s\\S]*?)(?=## |$)/);
return planMatch ? planMatch[1].trim() : '';
}
getNextADRNumber(): string {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}${month}${day}-001`;
}
}