/**
* generate_context tool implementation
*
* Generates context files in various formats with optional KG enrichment
*/
import { enrichContext, EnrichedContext } from './enrich-context.js';
import { analyzeCodebase } from './analyze-codebase.js';
import { writeFile, mkdir } from 'fs/promises';
import { join, dirname } from 'path';
import { existsSync } from 'fs';
interface GenerateContextArgs {
path: string;
format: 'cursorrules' | 'cursor_dir' | 'spec_md' | 'agents_md' | 'custom';
output_path?: string;
analysis_result?: any;
options?: Record<string, any>;
}
export async function generateContext(
args: GenerateContextArgs
): Promise<{ content: Array<{ type: string; text: string }> }> {
const { path, format, output_path, analysis_result, options = {} } = args;
// Get or perform analysis
let analysis = analysis_result;
if (!analysis) {
const analyzeResult = await analyzeCodebase({ path });
analysis = JSON.parse(analyzeResult.content[0].text);
}
// Enrich with knowledge graph if enabled (default: true)
let enriched: EnrichedContext | null = null;
if (options.enable_kg !== false) {
enriched = await enrichContext({
path,
analysis_result: analysis,
enrichment_level: options.enrichment_level || 'standard',
include_yago: options.include_yago !== false,
include_schema: options.include_schema !== false,
max_entities: options.max_entities || 10,
});
}
// Generate context based on format
let content: string;
let filename: string;
switch (format) {
case 'cursorrules':
content = generateCursorRules(analysis, enriched);
filename = '.cursorrules';
break;
case 'cursor_dir':
content = generateCursorDir(analysis, enriched);
filename = '.cursor/context.md';
break;
case 'spec_md':
content = generateSpecMd(analysis, enriched);
filename = 'SPEC.md';
break;
case 'agents_md':
content = generateAgentsMd(analysis, enriched);
filename = 'AGENTS.md';
break;
case 'custom':
content = generateCustom(analysis, enriched, options);
filename = options.filename || 'context.md';
break;
default:
throw new Error(`Unknown format: ${format}`);
}
// Write to file if output_path specified
if (output_path) {
const fullPath = join(output_path, filename);
const dir = dirname(fullPath);
if (!existsSync(dir)) {
await mkdir(dir, { recursive: true });
}
await writeFile(fullPath, content, 'utf-8');
}
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
message: 'Context generated successfully',
format,
path,
output_file: output_path ? join(output_path, filename) : null,
content_preview: content.slice(0, 500) + '...',
enriched: !!enriched,
kg_stats: enriched?.confidence_stats,
},
null,
2
),
},
],
};
}
/**
* Generate .cursorrules format
*/
function generateCursorRules(analysis: any, enriched: EnrichedContext | null): string {
const lines: string[] = [];
lines.push('# Project Rules for AI Assistants');
lines.push('');
// Project info
if (analysis.package?.name) {
lines.push(`## Project: ${analysis.package.name}`);
if (analysis.package.description) {
lines.push(analysis.package.description);
}
lines.push('');
}
// Schema.org annotation
if (enriched?.schema_annotation) {
lines.push('## Semantic Type');
lines.push(`This is a **${enriched.schema_annotation['@type']}**`);
if (enriched.schema_annotation.description) {
lines.push(enriched.schema_annotation.description);
}
lines.push('');
}
// Tech stack
lines.push('## Tech Stack');
if (analysis.languages) {
lines.push('**Languages:**', Object.keys(analysis.languages).join(', '));
}
if (analysis.frameworks && analysis.frameworks.length > 0) {
lines.push('**Frameworks:**', analysis.frameworks.join(', '));
}
lines.push('');
// Knowledge graph enrichment
if (enriched?.yago_entities && Object.keys(enriched.yago_entities).length > 0) {
lines.push('## Knowledge Graph Context');
for (const [name, entities] of Object.entries(enriched.yago_entities)) {
if (entities.length > 0) {
const entity = entities[0];
lines.push(`- **${name}**: ${entity.description || entity.label}`);
}
}
lines.push('');
}
// Coding conventions
lines.push('## Coding Conventions');
lines.push('- Follow existing patterns in the codebase');
lines.push('- Use TypeScript strict mode');
lines.push('- Write clear, self-documenting code');
lines.push('- Add JSDoc comments for public APIs');
lines.push('');
return lines.join('\n');
}
/**
* Generate .cursor/context.md format
*/
function generateCursorDir(analysis: any, enriched: EnrichedContext | null): string {
const lines: string[] = [];
lines.push('# Codebase Context');
lines.push('');
// Overview
lines.push('## Overview');
if (analysis.package?.description) {
lines.push(analysis.package.description);
}
lines.push('');
// JSON-LD structured data
if (enriched?.json_ld) {
lines.push('## Structured Data');
lines.push('```json');
lines.push(JSON.stringify(enriched.json_ld, null, 2));
lines.push('```');
lines.push('');
}
// Architecture
lines.push('## Architecture');
if (analysis.structure) {
lines.push('```');
lines.push(JSON.stringify(analysis.structure, null, 2));
lines.push('```');
}
lines.push('');
// Dependencies with KG context
if (analysis.package?.dependencies) {
lines.push('## Dependencies');
for (const [dep, version] of Object.entries(analysis.package.dependencies)) {
lines.push(`- **${dep}@${version}**`);
// Add KG context if available
if (enriched?.yago_entities?.[dep]) {
const entity = enriched.yago_entities[dep][0];
if (entity.description) {
lines.push(` - ${entity.description}`);
}
}
}
lines.push('');
}
return lines.join('\n');
}
/**
* Generate SPEC.md format
*/
function generateSpecMd(analysis: any, enriched: EnrichedContext | null): string {
const lines: string[] = [];
lines.push('# Specification');
lines.push('');
// Semantic annotation
if (enriched?.schema_annotation) {
lines.push('## Type');
lines.push(`**${enriched.schema_annotation['@type']}**`);
lines.push('');
}
// Purpose
lines.push('## Purpose');
lines.push(analysis.package?.description || 'No description available');
lines.push('');
// Technical specifications
lines.push('## Technical Specifications');
lines.push('');
if (enriched?.schema_annotation) {
if (enriched.schema_annotation.programmingLanguage) {
lines.push('**Programming Languages:**');
const langs = Array.isArray(enriched.schema_annotation.programmingLanguage)
? enriched.schema_annotation.programmingLanguage
: [enriched.schema_annotation.programmingLanguage];
lines.push(langs.map((l: string) => `- ${l}`).join('\n'));
lines.push('');
}
if (enriched.schema_annotation.softwareRequirements) {
lines.push('**Software Requirements:**');
lines.push(
enriched.schema_annotation.softwareRequirements
.slice(0, 10)
.map((r: string) => `- ${r}`)
.join('\n')
);
lines.push('');
}
}
// API surface
if (analysis.exports && analysis.exports.length > 0) {
lines.push('## Public API');
for (const exp of analysis.exports.slice(0, 20)) {
lines.push(`- \`${exp}\``);
}
lines.push('');
}
return lines.join('\n');
}
/**
* Generate AGENTS.md format (for multi-agent systems)
*/
function generateAgentsMd(analysis: any, enriched: EnrichedContext | null): string {
const lines: string[] = [];
lines.push('# Agent Context');
lines.push('');
lines.push('This document provides context for AI agents working on this codebase.');
lines.push('');
// Semantic context
if (enriched?.schema_annotation) {
lines.push('## Semantic Context');
lines.push(`**Type:** ${enriched.schema_annotation['@type']}`);
if (enriched.schema_annotation.name) {
lines.push(`**Name:** ${enriched.schema_annotation.name}`);
}
if (enriched.schema_annotation.softwareVersion) {
lines.push(`**Version:** ${enriched.schema_annotation.softwareVersion}`);
}
lines.push('');
}
// Knowledge graph entities
if (enriched?.yago_entities && Object.keys(enriched.yago_entities).length > 0) {
lines.push('## Knowledge Graph Entities');
lines.push('');
lines.push('The following entities have been linked to world knowledge:');
lines.push('');
for (const [name, entities] of Object.entries(enriched.yago_entities)) {
if (entities.length > 0) {
const entity = entities[0];
lines.push(`### ${name}`);
lines.push(`- **Type:** ${entity.type}`);
lines.push(`- **Confidence:** ${(entity.confidence * 100).toFixed(0)}%`);
if (entity.description) {
lines.push(`- **Description:** ${entity.description}`);
}
if (entity.facts.length > 0) {
lines.push(`- **Known Facts:** ${entity.facts.length}`);
for (const fact of entity.facts.slice(0, 3)) {
lines.push(` - ${fact.predicate}: ${fact.objectLabel || fact.object}`);
}
}
lines.push('');
}
}
}
// Codebase structure
lines.push('## Codebase Structure');
if (analysis.structure) {
lines.push('```json');
lines.push(JSON.stringify(analysis.structure, null, 2));
lines.push('```');
}
lines.push('');
return lines.join('\n');
}
/**
* Generate custom format based on options
*/
function generateCustom(
analysis: any,
enriched: EnrichedContext | null,
options: Record<string, any>
): string {
const template = options.template || 'default';
if (template === 'json') {
return JSON.stringify(
{
analysis,
enriched,
},
null,
2
);
}
// Default custom format
return `# Custom Context\n\n${JSON.stringify({ analysis, enriched }, null, 2)}`;
}