UNPKG

@claude-vector/core

Version:

Core vector search engine for code intelligence

641 lines (532 loc) 18 kB
/** * AI Metadata Manager - AI駆動開発に最適化されたメタデータ管理 * * 機能: * - 多次元メタデータ構造の管理 * - 関係性グラフの構築 * - 開発履歴の追跡 * - Claude最適化情報の生成 */ import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export class AIMetadataManager { constructor(options = {}) { this.options = { metadataPath: options.metadataPath || '.claude-ai-metadata', trackHistory: options.trackHistory ?? true, maxHistoryEntries: options.maxHistoryEntries || 100, enableRelationshipGraph: options.enableRelationshipGraph ?? true, ...options }; this.relationshipGraph = new Map(); this.historyCache = new Map(); this.qualityMetrics = new Map(); } /** * AI最適化メタデータを生成 */ async generateAIMetadata(chunk, filePath, developmentContext = {}) { const baseMetadata = chunk.metadata; const timestamp = new Date().toISOString(); // 基本的なAI最適化メタデータ const aiMetadata = { // 基本情報 id: this.generateChunkId(filePath, baseMetadata.startLine, baseMetadata.endLine), file: filePath, timestamp, language: baseMetadata.language, // セマンティック情報 semantic: { type: baseMetadata.semanticType || 'unknown', name: baseMetadata.name || 'anonymous', subtype: baseMetadata.subtype || 'general', scope: this.determineScope(baseMetadata), purpose: await this.inferPurpose(chunk.content, baseMetadata), patterns: this.extractPatterns(chunk.content, baseMetadata) }, // 複雑度とメトリクス complexity: { ...baseMetadata.complexity, readability: this.calculateReadability(chunk.content), maintainability: baseMetadata.complexity?.maintainabilityIndex || 0, testability: this.calculateTestability(chunk.content, baseMetadata) }, // 関係性情報 relationships: { dependencies: baseMetadata.dependencies || [], dependents: [], callGraph: this.buildCallGraph(chunk.content, baseMetadata), imports: baseMetadata.imports || [], exports: baseMetadata.exports || [], relatedFiles: [] }, // 開発コンテキスト development: { phase: developmentContext.phase || 'implementation', priority: this.calculatePriority(baseMetadata, developmentContext), stability: this.calculateStability(filePath, baseMetadata), changeFrequency: await this.getChangeFrequency(filePath), lastModified: timestamp, bugHistory: await this.getBugHistory(filePath, baseMetadata.startLine, baseMetadata.endLine), performanceMetrics: await this.getPerformanceMetrics(filePath, baseMetadata) }, // AI消費最適化 aiContext: { importance: this.calculateImportance(baseMetadata, developmentContext), searchability: this.calculateSearchability(baseMetadata), contextRelevance: this.calculateContextRelevance(baseMetadata, developmentContext), promptTemplate: this.selectPromptTemplate(baseMetadata), tokenEfficiency: this.calculateTokenEfficiency(chunk.content), claudeOptimization: { structured: true, annotated: true, contextual: true, priority: this.calculateClaudePriority(baseMetadata, developmentContext) } }, // 品質指標 quality: { codeQuality: this.assessCodeQuality(chunk.content, baseMetadata), documentation: this.assessDocumentation(chunk.content), testCoverage: await this.getTestCoverage(filePath, baseMetadata), codeSmells: this.detectCodeSmells(chunk.content, baseMetadata), bestPractices: this.checkBestPractices(chunk.content, baseMetadata) }, // 検索最適化 searchOptimization: { keywords: this.extractKeywords(chunk.content, baseMetadata), tags: this.generateTags(baseMetadata, developmentContext), searchWeight: this.calculateSearchWeight(baseMetadata), relevanceBoost: this.calculateRelevanceBoost(baseMetadata, developmentContext), indexingHints: this.generateIndexingHints(baseMetadata) } }; // 関係性グラフの更新 if (this.options.enableRelationshipGraph) { await this.updateRelationshipGraph(aiMetadata); } // 履歴の記録 if (this.options.trackHistory) { await this.recordHistory(aiMetadata); } return aiMetadata; } /** * チャンクIDの生成 */ generateChunkId(filePath, startLine, endLine) { const hash = this.simpleHash(`${filePath}:${startLine}:${endLine}`); return `chunk_${hash}`; } /** * 簡単なハッシュ関数 */ simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // 32bit整数に変換 } return Math.abs(hash).toString(36); } /** * スコープの決定 */ determineScope(metadata) { if (metadata.semanticType === 'class') return 'class'; if (metadata.semanticType === 'function') return 'function'; if (metadata.semanticType === 'export') return 'module'; if (metadata.semanticType === 'import') return 'module'; return 'local'; } /** * 目的の推論 */ async inferPurpose(content, metadata) { // 簡略化された目的推論 const purposeHints = { 'authentication': ['auth', 'login', 'password', 'token', 'verify'], 'data_processing': ['process', 'transform', 'parse', 'validate'], 'ui_component': ['component', 'render', 'jsx', 'tsx', 'props'], 'api_handler': ['api', 'endpoint', 'request', 'response', 'handler'], 'utility': ['util', 'helper', 'format', 'convert'], 'test': ['test', 'spec', 'expect', 'describe', 'it'], 'configuration': ['config', 'setting', 'option', 'env'] }; const lowerContent = content.toLowerCase(); for (const [purpose, hints] of Object.entries(purposeHints)) { if (hints.some(hint => lowerContent.includes(hint))) { return purpose; } } return 'general'; } /** * パターンの抽出 */ extractPatterns(content, metadata) { const patterns = []; // 設計パターンの検出 if (content.includes('constructor') && content.includes('this.')) { patterns.push('constructor_pattern'); } if (content.includes('Promise') || content.includes('async')) { patterns.push('async_pattern'); } if (content.includes('callback') || content.includes('=>')) { patterns.push('callback_pattern'); } if (content.includes('export') && content.includes('default')) { patterns.push('module_export_pattern'); } return patterns; } /** * 可読性の計算 */ calculateReadability(content) { const lines = content.split('\n'); const nonEmptyLines = lines.filter(line => line.trim().length > 0); const avgLineLength = nonEmptyLines.reduce((sum, line) => sum + line.length, 0) / nonEmptyLines.length; const commentRatio = lines.filter(line => line.trim().startsWith('//') || line.trim().startsWith('/*')).length / lines.length; // 簡略化された可読性スコア let readabilityScore = 0.5; // 行の長さによる調整 if (avgLineLength < 80) readabilityScore += 0.2; if (avgLineLength > 120) readabilityScore -= 0.2; // コメント率による調整 readabilityScore += commentRatio * 0.3; return Math.max(0, Math.min(1, readabilityScore)); } /** * テスト可能性の計算 */ calculateTestability(content, metadata) { let testability = 0.5; // 関数型プログラミングの特徴 if (metadata.semanticType === 'function' && !content.includes('this.')) { testability += 0.2; } // 副作用の検出 if (content.includes('console.') || content.includes('alert') || content.includes('window.')) { testability -= 0.2; } // 依存関係の少なさ if (metadata.dependencies && metadata.dependencies.length < 3) { testability += 0.1; } return Math.max(0, Math.min(1, testability)); } /** * 呼び出しグラフの構築 */ buildCallGraph(content, metadata) { const calls = []; const callRegex = /(\w+)\s*\(/g; let match; while ((match = callRegex.exec(content)) !== null) { calls.push(match[1]); } return [...new Set(calls)]; } /** * 優先度の計算 */ calculatePriority(metadata, context) { let priority = 0.5; // セマンティックタイプによる重み付け const typeWeights = { class: 0.9, function: 0.8, export: 0.7, import: 0.3 }; priority += (typeWeights[metadata.semanticType] || 0.2) * 0.3; // 複雑度による重み付け if (metadata.complexity) { priority += Math.min(metadata.complexity.cognitive / 50, 0.3); } // 開発フェーズによる重み付け if (context.phase === 'debugging') { priority += 0.2; } return Math.max(0, Math.min(1, priority)); } /** * 安定性の計算 */ calculateStability(filePath, metadata) { // 簡略化された安定性評価 if (metadata.semanticType === 'function' && metadata.complexity?.cognitive < 5) { return 0.8; } if (metadata.semanticType === 'class') { return 0.6; } return 0.5; } /** * 変更頻度の取得 */ async getChangeFrequency(filePath) { // 実際の実装では、gitログを解析して変更頻度を計算 return 0.3; // プレースホルダー } /** * バグ履歴の取得 */ async getBugHistory(filePath, startLine, endLine) { // 実際の実装では、gitログやissue trackingシステムを参照 return []; // プレースホルダー } /** * パフォーマンスメトリクスの取得 */ async getPerformanceMetrics(filePath, metadata) { // 実際の実装では、プロファイリングデータを参照 return { executionTime: 'unknown', memoryUsage: 'unknown', frequency: 'unknown' }; } /** * 重要度の計算 */ calculateImportance(metadata, context) { let importance = 0.5; // セマンティックタイプによる重み付け const typeWeights = { class: 0.9, function: 0.8, export: 0.7, import: 0.3 }; importance += (typeWeights[metadata.semanticType] || 0.2) * 0.4; // 複雑度による重み付け if (metadata.complexity) { importance += Math.min(metadata.complexity.cognitive / 100, 0.3); } // 依存関係による重み付け if (metadata.dependencies && metadata.dependencies.length > 0) { importance += Math.min(metadata.dependencies.length / 20, 0.3); } return Math.max(0, Math.min(1, importance)); } /** * 検索可能性の計算 */ calculateSearchability(metadata) { let searchability = 0.5; // 名前の質 if (metadata.name && metadata.name !== 'anonymous') { searchability += 0.3; } // セマンティックタイプ const searchableTypes = ['class', 'function', 'export']; if (searchableTypes.includes(metadata.semanticType)) { searchability += 0.2; } return Math.max(0, Math.min(1, searchability)); } /** * コンテキスト関連性の計算 */ calculateContextRelevance(metadata, context) { let relevance = 0.5; // 開発フェーズによる調整 if (context.phase === 'debugging' && metadata.complexity?.cognitive > 5) { relevance += 0.3; } if (context.phase === 'implementation' && metadata.semanticType === 'function') { relevance += 0.2; } return Math.max(0, Math.min(1, relevance)); } /** * プロンプトテンプレートの選択 */ selectPromptTemplate(metadata) { const templates = { class: 'class_structure', function: 'function_implementation', export: 'module_export', import: 'module_import', variable: 'variable_declaration' }; return templates[metadata.semanticType] || 'general_code'; } /** * トークン効率性の計算 */ calculateTokenEfficiency(content) { const tokens = content.length / 4; // 簡略化 const lines = content.split('\n').length; const efficiency = Math.min(tokens / lines, 1.0); return efficiency; } /** * Claude優先度の計算 */ calculateClaudePriority(metadata, context) { let priority = 0.5; // AI消費に適した構造 if (metadata.semanticType === 'function' || metadata.semanticType === 'class') { priority += 0.3; } // 複雑度による調整 if (metadata.complexity?.cognitive > 3 && metadata.complexity?.cognitive < 15) { priority += 0.2; } return Math.max(0, Math.min(1, priority)); } /** * その他のメソッド(簡略化) */ assessCodeQuality(content, metadata) { return 0.7; // プレースホルダー } assessDocumentation(content) { const commentRatio = content.split('\n').filter(line => line.trim().startsWith('//') || line.trim().startsWith('/*') ).length / content.split('\n').length; return Math.min(commentRatio * 2, 1.0); } async getTestCoverage(filePath, metadata) { return 0.5; // プレースホルダー } detectCodeSmells(content, metadata) { const smells = []; if (metadata.complexity?.cognitive > 10) { smells.push('high_complexity'); } if (content.length > 2000) { smells.push('long_method'); } if (metadata.dependencies && metadata.dependencies.length > 10) { smells.push('too_many_dependencies'); } return smells; } checkBestPractices(content, metadata) { const practices = []; if (content.includes('const ')) { practices.push('use_const'); } if (content.includes('async ') && content.includes('await ')) { practices.push('proper_async'); } if (content.includes('export ')) { practices.push('proper_exports'); } return practices; } extractKeywords(content, metadata) { const keywords = []; // セマンティック情報からキーワードを抽出 if (metadata.name) keywords.push(metadata.name); if (metadata.semanticType) keywords.push(metadata.semanticType); // 依存関係からキーワードを抽出 if (metadata.dependencies) { keywords.push(...metadata.dependencies); } return [...new Set(keywords)]; } generateTags(metadata, context) { const tags = []; tags.push(metadata.semanticType); tags.push(metadata.language); tags.push(context.phase || 'general'); if (metadata.complexity?.cognitive > 5) { tags.push('complex'); } return tags; } calculateSearchWeight(metadata) { let weight = 0.5; // 重要なセマンティックタイプの重み上げ if (metadata.semanticType === 'class' || metadata.semanticType === 'function') { weight += 0.3; } return weight; } calculateRelevanceBoost(metadata, context) { let boost = 0; // 開発フェーズによるブースト if (context.phase === 'debugging' && metadata.complexity?.cognitive > 5) { boost += 0.2; } return boost; } generateIndexingHints(metadata) { const hints = []; hints.push(`priority:${metadata.semanticType}`); if (metadata.complexity?.cognitive > 5) { hints.push('complex'); } return hints; } /** * 関係性グラフの更新 */ async updateRelationshipGraph(aiMetadata) { const chunkId = aiMetadata.id; // 依存関係の追加 aiMetadata.relationships.dependencies.forEach(dep => { if (!this.relationshipGraph.has(dep)) { this.relationshipGraph.set(dep, new Set()); } this.relationshipGraph.get(dep).add(chunkId); }); // 被依存関係の更新 for (const [nodeId, dependents] of this.relationshipGraph.entries()) { if (dependents.has(chunkId)) { aiMetadata.relationships.dependents.push(nodeId); } } } /** * 履歴の記録 */ async recordHistory(aiMetadata) { const chunkId = aiMetadata.id; if (!this.historyCache.has(chunkId)) { this.historyCache.set(chunkId, []); } const history = this.historyCache.get(chunkId); history.push({ timestamp: aiMetadata.timestamp, complexity: aiMetadata.complexity, quality: aiMetadata.quality }); // 履歴の制限 if (history.length > this.options.maxHistoryEntries) { history.shift(); } } /** * メタデータの保存 */ async saveMetadata(projectRoot, metadata) { const metadataDir = path.join(projectRoot, this.options.metadataPath); await fs.mkdir(metadataDir, { recursive: true }); const metadataFile = path.join(metadataDir, 'ai-metadata.json'); await fs.writeFile(metadataFile, JSON.stringify(metadata, null, 2)); } /** * メタデータの読み込み */ async loadMetadata(projectRoot) { const metadataDir = path.join(projectRoot, this.options.metadataPath); const metadataFile = path.join(metadataDir, 'ai-metadata.json'); try { const data = await fs.readFile(metadataFile, 'utf-8'); return JSON.parse(data); } catch (error) { return {}; } } }