github-mcp-auto-git
Version:
GitHub MCP Auto Git v3.0 - メモリ効率化・統合MCP・モジュール化完了の完全自動Git操作システム
421 lines • 19.5 kB
JavaScript
import { promises as fs } from 'fs';
import { join } from 'path';
import { IndependentSubAgents } from './independent-subagents.js';
import { MemoryEfficientExecutor } from './memory-efficient-executor.js';
export class SubAgentManager {
constructor(agentsPath = './src/agents', workingDir = process.cwd()) {
this.loadedAgents = new Map();
this.agentsPath = agentsPath;
this.workingDir = workingDir;
this.independentAgents = new IndependentSubAgents();
this.memoryExecutor = new MemoryEfficientExecutor({
maxConcurrentTasks: 3,
memoryThreshold: 256, // 256MB for subagent execution
gcThreshold: 80,
priorityQueues: true,
adaptiveTimeout: true,
memoryMonitoring: true
});
}
async loadAgent(agentName) {
if (this.loadedAgents.has(agentName)) {
return this.loadedAgents.get(agentName);
}
const agentPath = join(this.agentsPath, `${agentName}.md`);
const content = await fs.readFile(agentPath, 'utf-8');
const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontMatterMatch) {
throw new Error(`Invalid agent file format: ${agentName}`);
}
const yamlContent = frontMatterMatch[1];
if (!yamlContent) {
throw new Error(`Empty frontmatter in agent file: ${agentName}`);
}
const prompt = content.replace(/^---\n[\s\S]*?\n---\n/, '');
const metadata = {};
yamlContent.split('\n').forEach(line => {
const [key, ...valueParts] = line.split(':');
if (key && valueParts.length > 0) {
const value = valueParts.join(':').trim();
if (key.trim() === 'tools' && value.startsWith('[')) {
try {
// JSON配列として解析を試行
const cleanValue = value.replace(/['"]/g, '"'); // シングルクォートをダブルクォートに変換
const jsonValue = cleanValue.replace(/(\w+)/g, '"$1"'); // クォートされていない値をクォートで囲む
metadata[key.trim()] = JSON.parse(jsonValue);
}
catch {
// JSON解析に失敗した場合は、カンマ区切りの配列として解析
const arrayValue = value.replace(/[\[\]]/g, '').split(',').map(item => item.trim().replace(/['"]/g, ''));
metadata[key.trim()] = arrayValue.filter(item => item.length > 0);
}
}
else {
metadata[key.trim()] = value.replace(/['"]/g, '');
}
}
});
const agent = {
name: metadata.name,
description: metadata.description,
version: metadata.version,
tools: metadata.tools || [],
prompt
};
this.loadedAgents.set(agentName, agent);
return agent;
}
async executeAgent(agentName, userPrompt, context = {}) {
const startTime = Date.now();
try {
const agent = await this.loadAgent(agentName);
// このシステムはClaude Codeのサブエージェント機能と連携するために設計されています
// 実際の実行時は、Claude Codeが自動的にサブエージェントを委譲します
// フォールバック処理:Claude Code外での実行時は簡易分析を提供
const fullPrompt = `${userPrompt}\n\nContext: ${JSON.stringify(context, null, 2)}`;
console.log(`📝 サブエージェント ${agentName} を実行しています...`);
console.log(`📋 タスク: ${userPrompt}`);
// 簡易的な分析結果を生成(Claude Codeのサブエージェント機能がない場合)
const result = this.generateFallbackResult(agentName, context);
return {
agentName: agent.name,
result,
executionTime: Date.now() - startTime,
confidence: 0.6 // フォールバック処理なので低めの信頼度
};
}
catch (error) {
return {
agentName,
result: null,
executionTime: Date.now() - startTime,
confidence: 0,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Execute multiple agents in parallel with memory optimization
* Fail Fast: Resource validation and task prioritization
* Be Lazy: Memory-aware batch execution
* TypeScript First: Complete type safety for parallel execution
*/
async executeParallel(agentExecutions) {
console.log(`🔄 メモリ効率化並列実行開始: ${agentExecutions.length}エージェント`);
// Convert agent executions to memory-optimized tasks
const tasks = agentExecutions.map((execution, index) => ({
id: `agent-${execution.agentName}-${index}`,
priority: this.getAgentPriority(execution.agentName),
timeout: this.getAgentTimeout(execution.agentName),
retryAttempts: 2,
memoryLimit: this.getAgentMemoryEstimate(execution.agentName),
executor: () => this.executeAgent(execution.agentName, execution.userPrompt, execution.context),
cleanup: async () => {
// Agent-specific cleanup (e.g., close file handles, clear caches)
console.log(`🧹 エージェント ${execution.agentName} リソースクリーンアップ`);
}
}));
// Execute with memory optimization
const results = await this.memoryExecutor.executeParallel(tasks);
// Convert execution results back to agent results
return results.map(result => {
if (result.success && result.result) {
return result.result;
}
else {
// Create fallback agent result for failed executions
return {
agentName: result.taskId.split('-')[1] || 'unknown',
result: null,
executionTime: result.executionTime,
confidence: 0,
error: result.error || 'Execution failed'
};
}
});
}
async analyzeSafety(context) {
try {
// 高品質な独立実装を使用
console.log(`🔍 高品質安全性分析を実行中... (ファイル数: ${context.files.length})`);
const result = await this.independentAgents.analyzeSafety(context.files, this.workingDir);
console.log(`✅ 安全性分析完了 (スコア: ${result.safetyScore}, レベル: ${result.level})`);
return result;
}
catch (error) {
console.warn(`⚠️ 独立分析に失敗、フォールバックを使用: ${error}`);
// フォールバックとして既存実装を使用
const result = await this.executeAgent('git-safety-analyzer', `以下の変更内容の安全性を分析してください。機密情報の検出、破壊的変更の確認、ファイルサイズのチェックを行い、安全性スコアと推奨事項を提供してください。`, context);
return result.result;
}
}
async generateCommitMessage(context) {
try {
// 高品質な独立実装を使用
console.log(`📝 高品質コミットメッセージ生成中... (変更タイプ: ${context.changes.type})`);
const result = await this.independentAgents.generateCommitMessage(context.changes, context.files);
console.log(`✅ コミットメッセージ生成完了: "${result.title}"`);
return result;
}
catch (error) {
console.warn(`⚠️ 独立分析に失敗、フォールバックを使用: ${error}`);
// フォールバックとして既存実装を使用
const result = await this.executeAgent('commit-message-generator', `以下の変更内容に基づいて、非エンジニアにも理解できるコミットメッセージを生成してください。変更の種類、影響範囲、効果を分かりやすく説明してください。`, context);
return result.result;
}
}
async managePR(context) {
try {
// 高品質な独立実装を使用
console.log(`🔀 高品質PR管理戦略決定中... (影響度: ${context.changes.impact})`);
const result = await this.independentAgents.generatePRManagement(context.changes, [], // ファイル一覧は changes に含まれているため空配列
context.commitMessage.title);
console.log(`✅ PR管理戦略決定完了 (自動マージ: ${result.autoMerge})`);
return result;
}
catch (error) {
console.warn(`⚠️ 独立分析に失敗、フォールバックを使用: ${error}`);
// フォールバックとして既存実装を使用
const result = await this.executeAgent('pr-management-agent', `プルリクエストの管理戦略を決定してください。変更内容、安全性分析、コミットメッセージを総合的に判断し、適切なマージ戦略、レビュアー、ラベルを提案してください。`, context);
return result.result;
}
}
async executeGitWorkflow(context) {
const startTime = Date.now();
const errors = [];
try {
console.log('🚀 Git ワークフロー並列実行開始 (メモリ最適化)');
const [safetyResult, commitResult] = await this.executeParallel([
{
agentName: 'git-safety-analyzer',
userPrompt: '変更内容の安全性を分析してください。',
context: { files: context.files, diff: context.diff, changes: context.changes }
},
{
agentName: 'commit-message-generator',
userPrompt: 'コミットメッセージを生成してください。',
context: { changes: context.changes, diff: context.diff, files: context.files }
}
]);
if (safetyResult?.error) {
errors.push(`Safety analysis failed: ${safetyResult.error}`);
}
if (commitResult?.error) {
errors.push(`Commit message generation failed: ${commitResult.error}`);
}
const safety = safetyResult?.result;
const commitMessage = commitResult?.result;
let prManagement;
if (safety && commitMessage) {
const prResult = await this.executeAgent('pr-management-agent', 'PR管理戦略を決定してください。', {
changes: context.changes,
safety,
commitMessage,
branchName: context.branchName,
targetBranch: context.targetBranch || 'main'
});
if (prResult.error) {
errors.push(`PR management failed: ${prResult.error}`);
}
prManagement = prResult.result;
}
else {
prManagement = this.createFallbackPRManagement(context);
}
return {
safety: safety || this.createFallbackSafety(),
commitMessage: commitMessage || this.createFallbackCommitMessage(),
prManagement,
executionTime: Date.now() - startTime,
errors
};
}
catch (error) {
errors.push(`Workflow execution failed: ${error}`);
return {
safety: this.createFallbackSafety(),
commitMessage: this.createFallbackCommitMessage(),
prManagement: this.createFallbackPRManagement(context),
executionTime: Date.now() - startTime,
errors
};
}
}
extractConfidence(result) {
const confidenceMatch = result.match(/"confidence":\s*([0-9.]+)/);
return confidenceMatch && confidenceMatch[1] ? parseFloat(confidenceMatch[1]) : 0.5;
}
generateFallbackResult(agentName, context) {
switch (agentName) {
case 'git-safety-analyzer':
return {
safetyScore: 75,
level: 'SAFE',
risks: [],
recommendations: ['Claude Codeのサブエージェント機能を使用することを推奨します'],
autoApprove: true,
confidence: 0.6
};
case 'commit-message-generator':
return {
title: '変更: ファイルを更新',
body: 'ファイルの内容が更新されました。\n\n詳細な分析にはClaude Codeのサブエージェント機能をご利用ください。',
conventional: 'chore: update files',
confidence: 0.6
};
case 'pr-management-agent':
return {
prTitle: '変更: ファイル更新',
prBody: 'ファイルの変更が含まれています。\n\n詳細な分析結果を表示するには、Claude Codeのサブエージェント機能をお使いください。',
autoMerge: false,
mergeStrategy: 'squash',
reviewers: [],
labels: ['needs-review'],
assignees: [],
deleteBranch: true,
reasoning: 'フォールバック処理のため手動レビューが必要です'
};
default:
return {};
}
}
createFallbackSafety() {
return {
safetyScore: 50,
level: 'WARNING',
risks: [{
type: 'conflict_risk',
severity: 'medium',
description: 'サブエージェント分析が失敗しました。手動で確認してください。',
file: '',
suggestion: '変更内容を手動で確認し、安全性を判断してください。'
}],
recommendations: ['変更内容の手動確認を推奨します'],
autoApprove: false,
confidence: 0.1
};
}
createFallbackCommitMessage() {
return {
title: '変更: ファイルを更新',
body: 'ファイルの内容を更新しました。\n\n詳細は変更内容をご確認ください。',
conventional: 'chore: update files',
confidence: 0.1
};
}
createFallbackPRManagement(context) {
return {
prTitle: `変更: ${context.branchName} の更新`,
prBody: 'ファイルの変更を含むプルリクエストです。\n\n詳細な変更内容をレビューしてください。',
autoMerge: false,
mergeStrategy: 'squash',
reviewers: [],
labels: ['review-required'],
assignees: [],
deleteBranch: true,
reasoning: 'サブエージェント分析が失敗したため、手動レビューが必要です。'
};
}
async getAgentStatus() {
const available = [];
const errors = [];
try {
const files = await fs.readdir(this.agentsPath);
const mdFiles = files.filter(f => f.endsWith('.md'));
for (const file of mdFiles) {
const agentName = file.replace('.md', '');
try {
await this.loadAgent(agentName);
available.push(agentName);
}
catch (error) {
errors.push(`${agentName}: ${error}`);
}
}
}
catch (error) {
errors.push(`Failed to read agents directory: ${error}`);
}
return {
loaded: Array.from(this.loadedAgents.keys()),
available,
errors
};
}
/**
* Add cleanup method for memory executor
* Fail Fast: Comprehensive cleanup with error handling
*/
async cleanup() {
try {
console.log('🧹 SubAgent Manager クリーンアップ中...');
await this.memoryExecutor.shutdown();
console.log('✅ SubAgent Manager クリーンアップ完了');
}
catch (error) {
console.warn('⚠️ SubAgent Manager クリーンアップ失敗:', error);
}
}
/**
* Get memory statistics from executor
* Be Lazy: Efficient memory monitoring
*/
getMemoryStats() {
return this.memoryExecutor.getMemoryStats();
}
/**
* Determine agent priority based on agent type
* Critical: git-safety-analyzer (security critical)
* High: commit-message-generator (user-facing)
* Medium: pr-management-agent (automation)
*/
getAgentPriority(agentName) {
switch (agentName) {
case 'git-safety-analyzer':
return 'critical'; // Security analysis is highest priority
case 'commit-message-generator':
return 'high'; // User-facing output is high priority
case 'pr-management-agent':
return 'medium'; // Automation is medium priority
default:
return 'low';
}
}
/**
* Estimate timeout for agent based on complexity
* Safety analysis: 45s (complex file analysis)
* Commit message: 30s (text generation)
* PR management: 20s (decision making)
*/
getAgentTimeout(agentName) {
switch (agentName) {
case 'git-safety-analyzer':
return 45000; // 45 seconds for complex analysis
case 'commit-message-generator':
return 30000; // 30 seconds for text generation
case 'pr-management-agent':
return 20000; // 20 seconds for strategy decision
default:
return 15000; // 15 seconds default
}
}
/**
* Estimate memory usage for agent based on typical operations
* Safety analysis: 64MB (file reading + analysis)
* Commit message: 32MB (text processing)
* PR management: 24MB (decision logic)
*/
getAgentMemoryEstimate(agentName) {
switch (agentName) {
case 'git-safety-analyzer':
return 64; // MB - File analysis requires more memory
case 'commit-message-generator':
return 32; // MB - Text generation requires moderate memory
case 'pr-management-agent':
return 24; // MB - Strategy decisions require less memory
default:
return 16; // MB - Default conservative estimate
}
}
}
//# sourceMappingURL=subagent-manager.js.map