github-mcp-auto-git
Version:
GitHub MCP Auto Git v3.0 - メモリ効率化・統合MCP・モジュール化完了の完全自動Git操作システム
388 lines • 16.3 kB
JavaScript
/**
* Resilient Executor - 堅牢な実行環境
* 重要な操作に対する包括的なエラーハンドリングとリカバリー
*/
import { ErrorRecoverySystem } from './error-recovery.js';
export class ResilientExecutor {
constructor() {
this.executionHistory = new Map();
this.errorRecovery = new ErrorRecoverySystem();
}
/**
* 堅牢な関数実行
*/
async execute(operation, context, options = {}) {
const startTime = Date.now();
const maxRetries = options.maxRetries || 3;
let timeoutMs = options.timeoutMs || this.calculateOptimalTimeout(context.name, options);
const warnings = [];
let attempts = 0;
let lastError = null;
console.log(`🚀 堅牢実行開始: ${context.name} (最大${maxRetries}回試行)`);
for (attempts = 1; attempts <= maxRetries; attempts++) {
try {
// 適応的タイムアウト調整
if (options.adaptiveTimeout && attempts > 1) {
timeoutMs = this.adjustTimeoutForRetry(timeoutMs, attempts, context.name);
}
// Claude Code最適化処理
if (options.claudeCodeOptimized) {
await this.optimizeForClaudeCode(context, options);
}
// タイムアウト付き実行
const result = await this.executeWithTimeout(operation, timeoutMs);
const executionTime = Date.now() - startTime;
console.log(`✅ 実行成功: ${context.name} (${attempts}回目, ${executionTime}ms)`);
// 実行履歴を記録(適応的タイムアウトのため)
this.recordExecutionTime(context.name, executionTime);
return {
success: true,
data: result,
attempts,
executionTime,
warnings
};
}
catch (error) {
lastError = error;
console.warn(`⚠️ 実行失敗 ${attempts}/${maxRetries}: ${lastError.message}`);
// クリティカル操作で重大エラーの場合は即座に停止
if (options.critical && this.isCriticalError(lastError)) {
console.error(`🚨 クリティカルエラー検出、実行停止: ${lastError.message}`);
break;
}
// 最後の試行でない場合はエラーハンドリング実行
if (attempts < maxRetries) {
try {
await this.errorRecovery.handleError(lastError, {
operation: context.name,
timestamp: new Date(),
workingDir: context.workingDir,
files: context.files,
attempt: attempts,
metadata: context.metadata
});
warnings.push(`リトライ ${attempts}: ${lastError.message}`);
}
catch (recoveryError) {
warnings.push(`リカバリー失敗 ${attempts}: ${recoveryError}`);
// リカバリーが失敗してもリトライは続行
}
}
}
}
// すべてのリトライが失敗した場合
const executionTime = Date.now() - startTime;
console.error(`❌ 実行完全失敗: ${context.name} (${attempts}回試行, ${executionTime}ms)`);
// フォールバックが必要な場合
if (options.fallbackRequired && lastError) {
console.log(`🔄 フォールバック処理を試行中...`);
try {
const fallbackResult = await this.executeFallback(context, lastError);
warnings.push('フォールバック処理を使用しました');
return {
success: true,
data: fallbackResult,
attempts,
executionTime,
warnings
};
}
catch (fallbackError) {
warnings.push(`フォールバック失敗: ${fallbackError}`);
}
}
return {
success: false,
error: lastError || new Error('Unknown error'),
attempts,
executionTime,
warnings
};
}
/**
* タイムアウト付き実行
*/
async executeWithTimeout(operation, timeoutMs) {
return Promise.race([
operation(),
new Promise((_, reject) => setTimeout(() => reject(new Error(`Operation timeout after ${timeoutMs}ms`)), timeoutMs))
]);
}
/**
* クリティカルエラー判定
*/
isCriticalError(error) {
const message = error.message.toLowerCase();
const criticalPatterns = [
'secret', 'credential', 'password', 'token',
'permission denied', 'access denied', 'unauthorized',
'security', 'destructive', 'dangerous'
];
return criticalPatterns.some(pattern => message.includes(pattern));
}
/**
* フォールバック処理
*/
async executeFallback(context, originalError) {
const fallbackStrategies = {
'git-commit': async () => ({
success: false,
message: 'Git commit failed, changes staged but not committed',
details: { fallback: true, originalError: originalError.message }
}),
'github-pr': async () => ({
success: false,
message: 'GitHub PR creation failed, manual creation required',
details: { fallback: true, originalError: originalError.message }
}),
'safety-analysis': async () => ({
safetyScore: 50,
level: 'WARNING',
risks: [{
type: 'analysis_failed',
severity: 'medium',
description: '安全性分析が失敗しました。手動確認が必要です。'
}],
recommendations: ['手動で変更内容を確認してください'],
autoApprove: false,
confidence: 0.1
}),
'commit-message': async () => ({
title: '変更: ファイルを更新(自動生成失敗)',
body: '自動メッセージ生成に失敗しました。詳細は変更内容をご確認ください。',
conventional: 'chore: update files (auto-generation failed)',
confidence: 0.1
})
};
const strategy = fallbackStrategies[context.name];
if (strategy) {
console.log(`📋 フォールバック戦略実行: ${context.name}`);
return await strategy();
}
throw new Error(`No fallback strategy available for: ${context.name}`);
}
/**
* バッチ実行(複数操作の堅牢な実行)
*/
async executeBatch(operations) {
console.log(`🔄 バッチ実行開始: ${operations.length}個の操作`);
const results = [];
let criticalFailure = false;
for (const op of operations) {
if (criticalFailure) {
// クリティカルエラー後は残りをスキップ
results.push({
success: false,
error: new Error('Skipped due to critical failure'),
attempts: 0,
executionTime: 0,
warnings: ['Previous critical failure caused skip']
});
continue;
}
const result = await this.execute(op.operation, {
name: op.name,
...op.context
}, op.options);
results.push(result);
// クリティカルエラーの場合は後続処理を停止
if (!result.success && op.options?.critical) {
console.error(`🛑 クリティカル操作失敗、バッチ処理を停止: ${op.name}`);
criticalFailure = true;
}
}
const successCount = results.filter(r => r.success).length;
console.log(`✅ バッチ実行完了: ${successCount}/${operations.length} 成功`);
return results;
}
/**
* 状況把握とレポート
*/
getHealthReport() {
const errorStats = this.errorRecovery.getErrorStatistics();
const systemHealth = this.errorRecovery.checkSystemHealth();
const recommendations = [...systemHealth.recommendations];
// 追加の推奨事項
if (errorStats.byLevel.high > 5) {
recommendations.push('高レベルエラーが多発しています。システム設定を確認してください。');
}
if (errorStats.avgResolutionTime > 10000) {
recommendations.push('エラー解決時間が長くなっています。パフォーマンスを確認してください。');
}
return {
status: systemHealth.status,
errorStats,
systemHealth,
recommendations
};
}
/**
* 緊急停止(Emergency Stop)
*/
emergencyStop(reason) {
console.error(`🚨 緊急停止実行: ${reason}`);
// 実際の実装では、進行中の全操作を停止する処理を追加
process.exit(1);
}
/**
* Claude Code最適化処理
*/
async optimizeForClaudeCode(context, options) {
// Claude Code環境での最適化
if (process.env.CLAUDE_CODE_SESSION) {
console.log('🔧 Claude Code環境最適化を適用中...');
// プライオリティに基づくリソース調整
if (options.priorityLevel === 'critical') {
// クリティカル操作の場合、他の処理を一時停止
await this.pauseNonCriticalOperations();
}
// メモリ使用量の最適化
if (global.gc && context.metadata?.memoryIntensive) {
console.log('🧹 メモリ最適化: ガベージコレクション実行');
global.gc();
}
// ファイル数が多い場合のバッチ処理最適化
if (context.files && context.files.length > 50) {
console.log(`📦 大量ファイル処理最適化: ${context.files.length}ファイル`);
context.metadata = {
...context.metadata,
batchProcessing: true,
chunkSize: Math.min(20, Math.ceil(context.files.length / 4))
};
}
}
}
/**
* 最適タイムアウト計算
*/
calculateOptimalTimeout(operationName, options) {
const baseTimeouts = {
'safety-analysis': 45000,
'commit-message-generation': 30000,
'pr-management': 60000,
'github-operations': 90000,
'git-operations': 30000,
'file-analysis': 25000
};
let baseTimeout = baseTimeouts[operationName] || 30000;
// プライオリティに基づく調整
switch (options.priorityLevel) {
case 'critical':
baseTimeout *= 2; // クリティカル操作は十分な時間を確保
break;
case 'high':
baseTimeout *= 1.5;
break;
case 'low':
baseTimeout *= 0.7; // 低優先度は短縮
break;
}
// 履歴に基づく適応的調整
if (options.adaptiveTimeout) {
const avgTime = this.getAverageExecutionTime(operationName);
if (avgTime > 0) {
baseTimeout = Math.max(baseTimeout, avgTime * 1.8); // 平均時間の1.8倍をタイムアウトに
}
}
// Claude Code環境での調整
if (options.claudeCodeOptimized && process.env.CLAUDE_CODE_SESSION) {
baseTimeout *= 1.3; // Claude Code環境では余裕をもたせる
}
return Math.min(baseTimeout, 300000); // 最大5分
}
/**
* リトライ時のタイムアウト調整
*/
adjustTimeoutForRetry(currentTimeout, attempt, operationName) {
// リトライ時は段階的にタイムアウトを延長
const multiplier = 1 + (attempt - 1) * 0.5; // 1回目: 1.0x, 2回目: 1.5x, 3回目: 2.0x
const adjustedTimeout = Math.floor(currentTimeout * multiplier);
console.log(`⏱️ タイムアウト調整 ${operationName}: ${currentTimeout}ms → ${adjustedTimeout}ms (試行${attempt}回目)`);
return Math.min(adjustedTimeout, 300000); // 最大5分
}
/**
* 実行時間記録
*/
recordExecutionTime(operationName, executionTime) {
if (!this.executionHistory.has(operationName)) {
this.executionHistory.set(operationName, []);
}
const history = this.executionHistory.get(operationName);
history.push(executionTime);
// 直近20回の記録のみ保持
if (history.length > 20) {
history.shift();
}
}
/**
* 平均実行時間取得
*/
getAverageExecutionTime(operationName) {
const history = this.executionHistory.get(operationName);
if (!history || history.length === 0) {
return 0;
}
const sum = history.reduce((acc, time) => acc + time, 0);
return Math.floor(sum / history.length);
}
/**
* 非クリティカル操作の一時停止
*/
async pauseNonCriticalOperations() {
// 実装例: ファイル監視の一時停止、定期処理の延期など
console.log('⏸️ 非クリティカル操作を一時停止');
// 実際の実装では、システム全体の状態管理が必要
}
/**
* パフォーマンス統計取得
*/
getPerformanceStats() {
const operations = {};
for (const [operationName, times] of this.executionHistory.entries()) {
operations[operationName] = {
averageTime: this.getAverageExecutionTime(operationName),
totalExecutions: times.length,
successRate: 1.0 // 成功した実行のみ記録されるため100%
};
}
// システム健全性判定
const avgTimes = Object.values(operations).map((op) => op.averageTime);
const maxAvgTime = Math.max(...avgTimes, 0);
let systemHealth = 'optimal';
if (maxAvgTime > 60000)
systemHealth = 'critical';
else if (maxAvgTime > 30000)
systemHealth = 'warning';
else if (maxAvgTime > 15000)
systemHealth = 'good';
return {
operations,
systemHealth
};
}
/**
* メンテナンス関数
*/
async performMaintenance() {
console.log('🧹 定期メンテナンス実行中...');
const errorsCleared = this.errorRecovery.clearOldErrors(24); // 24時間前のエラーをクリア
const healthReport = this.getHealthReport();
// パフォーマンス履歴のクリーンアップ
let performanceOptimized = false;
for (const [operationName, times] of this.executionHistory.entries()) {
if (times.length > 50) {
// 古い履歴を削除
times.splice(0, times.length - 20);
performanceOptimized = true;
}
}
console.log(`✅ メンテナンス完了: ${errorsCleared}個のエラーをクリア, パフォーマンス最適化: ${performanceOptimized}`);
return {
errorsCleared,
status: healthReport.status,
performanceOptimized
};
}
}
//# sourceMappingURL=resilient-executor.js.map