@claude-vector/core
Version:
Core vector search engine for code intelligence
641 lines (532 loc) • 18 kB
JavaScript
/**
* 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 {};
}
}
}