import fs from 'fs';
import path from 'path';
import { DevelopmentContext } from '../types';
import { ToolDiscoveryService, DiscoveredTool } from './tool-discovery.service';
/**
* Service for managing development context
*/
export class ContextService {
private developmentContext: DevelopmentContext | null = null;
private toolDiscoveryService = new ToolDiscoveryService();
/**
* Initialize the context service
* @param contextPath - Path to the context file
*/
constructor(
private contextPath: string = path.join(process.cwd(), 'context.json')
) {
// Normalize path to ensure consistent behavior across platforms
this.contextPath = path.resolve(this.contextPath);
console.error(`Context path set to: ${this.contextPath}`);
this.generateContextFromTools();
}
/**
* Generate context.json dynamically from discovered tools
*/
private generateContextFromTools(): void {
const tools = this.toolDiscoveryService.discoverTools();
const context: DevelopmentContext = {
name: 'Claude Tool Context',
description: 'This context is dynamically generated from the tools currently installed in Claude. It lists available tools, their capabilities, and usage patterns for code generation and automation.',
version: 'auto',
directives: [
'Each tool is listed with its trigger keywords and description.',
'Use trigger keywords to invoke the correct tool from user prompts.'
],
workflows: [],
templates: []
};
// Add each tool as a workflow for discoverability
context.workflows = tools.map((tool, idx) => ({
id: tool.name.replace(/\s+/g, '-').toLowerCase(),
name: tool.name,
description: tool.description + ` (Server: ${tool.server})`,
steps: tool.triggerKeywords.map((kw, i) => ({ order: i + 1, description: `Trigger: ${kw}` }))
}));
this.developmentContext = context;
this.saveContext();
}
/**
* Load development context from file
*/
private loadContext(): void {
// Always regenerate context from tools
this.generateContextFromTools();
}
/**
* Get the current development context
* @returns The current development context
*/
public getContext(): DevelopmentContext | null {
return this.developmentContext;
}
/**
* Update the development context
* @param newContext - New context data
*/
public updateContext(newContext: Partial<DevelopmentContext>): void {
// Regenerate context from tools, ignore manual updates
this.generateContextFromTools();
}
/**
* Save the current context to file
*/
private saveContext(): void {
try {
if (this.developmentContext) {
fs.writeFileSync(this.contextPath, JSON.stringify(this.developmentContext, null, 2));
}
} catch (error) {
console.error('Error saving development context:',
error instanceof Error ? error.message : String(error));
}
}
/**
* Check if a message contains trigger keywords
* @param message - The message to check
* @param triggerKeywords - Keywords to look for
* @returns Whether the message contains any trigger keywords
*/
public shouldInjectContext(message: string, triggerKeywords: string[]): boolean {
return triggerKeywords.some(keyword =>
message.toLowerCase().includes(keyword.toLowerCase())
);
}
/**
* Format the context for injection into a conversation
* @returns Formatted context as a string
*/
public async formatContextForInjection(): Promise<string> {
if (!this.developmentContext) {
return 'No development context available. Please ensure a valid context file exists.';
}
let formattedContext = `\n# ${this.developmentContext.name}\n\n${this.developmentContext.description}\n\n## Tool List\n`;
this.developmentContext.workflows.forEach(workflow => {
formattedContext += `\n### ${workflow.name}\n${workflow.description}\n\nTrigger Keywords:\n${workflow.steps.map(step => `- ${step.description}`).join('\n')}\n`;
});
return formattedContext;
}
}