Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 17:52:09 +08:00
commit 863b553c2a
20 changed files with 4544 additions and 0 deletions

View File

@@ -0,0 +1,273 @@
#!/usr/bin/env node
/**
* 実装後のチェックフック
*
* このフックは /implement コマンド実行後に自動的に実行され、
* テストの成功を確認します。
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// テストの実行
function runTests() {
console.log('🧪 テストを実行中...\n');
const commands = [
{ check: 'package.json', command: 'npm test', name: 'npm test' },
{ check: 'pytest.ini', command: 'pytest', name: 'pytest' },
{ check: 'go.mod', command: 'go test ./...', name: 'go test' },
{ check: 'pom.xml', command: 'mvn test', name: 'mvn test' },
{ check: 'Gemfile', command: 'bundle exec rspec', name: 'rspec' },
];
for (const { check, command, name } of commands) {
if (fs.existsSync(check)) {
try {
console.log(`実行中: ${name}\n`);
const output = execSync(command, {
encoding: 'utf-8',
stdio: 'pipe',
maxBuffer: 10 * 1024 * 1024, // 10MB
});
console.log(output);
return { success: true, output, command: name };
} catch (error) {
console.error(`❌ テストが失敗しました\n`);
console.error(error.stdout || error.message);
return {
success: false,
output: error.stdout || error.message,
command: name,
error: error.message,
};
}
}
}
console.log('⚠️ テスト実行コマンドが見つかりません');
return { success: false, output: 'テストコマンドが見つかりません' };
}
// カバレッジの確認
function checkCoverage() {
console.log('\n📊 コードカバレッジを確認中...\n');
const commands = [
{
check: 'package.json',
command: 'npm test -- --coverage --silent',
name: 'Jest coverage',
},
{
check: 'pytest.ini',
command: 'pytest --cov=. --cov-report=term-missing',
name: 'pytest coverage',
},
{
check: 'go.mod',
command: 'go test -cover ./...',
name: 'Go coverage',
},
];
for (const { check, command, name } of commands) {
if (fs.existsSync(check)) {
try {
console.log(`実行中: ${name}\n`);
const output = execSync(command, {
encoding: 'utf-8',
stdio: 'pipe',
maxBuffer: 10 * 1024 * 1024,
});
console.log(output);
// カバレッジ率の抽出(簡易的)
const coverageMatch = output.match(/(\d+\.?\d*)%/);
if (coverageMatch) {
const coverage = parseFloat(coverageMatch[1]);
return {
success: true,
coverage,
output,
sufficient: coverage >= 80,
};
}
return { success: true, coverage: null, output };
} catch (error) {
console.error('⚠️ カバレッジの取得に失敗しました');
console.error(error.stdout || error.message);
return { success: false, error: error.message };
}
}
}
console.log('⚠️ カバレッジコマンドが見つかりません');
return { success: false };
}
// メイン処理
function main() {
console.log('✨ 実装のテストを実行中...\n');
console.log('=' .repeat(50));
// テストの実行
const testResult = runTests();
if (!testResult.success) {
console.error('\n❌ 実装が完了していません');
console.error(' テストを通すまで実装を続けてください\n');
console.error('TDDのフロー:');
console.error(' 1. テストが失敗しているRed');
console.error(' 2. 実装してテストを通すGreen ← 今ここ');
console.error(' 3. リファクタリング');
process.exit(1);
}
console.log('\n✅ すべてのテストが成功しました!');
// ステップ3: カバレッジの確認
console.log('\n' + '='.repeat(50));
const coverageResult = checkCoverage();
if (coverageResult.success && coverageResult.coverage !== null) {
if (coverageResult.sufficient) {
console.log(`\n✅ カバレッジ: ${coverageResult.coverage}% (目標: 80%以上)`);
} else {
console.log(`\n⚠️ カバレッジ: ${coverageResult.coverage}% (目標: 80%以上)`);
console.log(' テストを追加してカバレッジを向上させることを推奨');
}
}
// 最終サマリー
console.log('\n' + '='.repeat(50));
console.log('\n✅ 実装フェーズが完了しました\n');
console.log('✅ すべてのテストが成功');
if (coverageResult.coverage) {
console.log(`✅ コードカバレッジ: ${coverageResult.coverage}%`);
}
console.log('\n次のステップ:');
console.log(' 1. コードレビューを依頼');
console.log(' 2. ドキュメントを更新');
console.log(' 3. コミット & プッシュ');
console.log(' 4. プルリクエストを作成(該当する場合)');
// タスク状態の自動更新
updateTaskStatus(4, 'completed');
}
// タスク状態を更新する関数
function updateTaskStatus(taskId, status) {
const tasksFile = '.tasks.json';
const historyFile = '.tasks-history.json';
if (!fs.existsSync(tasksFile)) {
console.log('\n⚠ タスクファイルが見つかりません。タスク状態は更新されませんでした。');
return;
}
try {
const data = JSON.parse(fs.readFileSync(tasksFile, 'utf-8'));
const taskIndex = data.tasks.findIndex(t => t.id === taskId);
if (taskIndex === -1) {
console.log(`\n⚠️ タスク #${taskId} が見つかりません。`);
return;
}
const task = data.tasks[taskIndex];
task.status = status;
task.completedAt = new Date().toISOString();
console.log(`\n📋 タスク #${taskId} (${task.name}) を完了としてマークしました`);
// 完了したタスクを履歴に移動
if (status === 'completed') {
moveTaskToHistory(data.feature, task, historyFile);
// アクティブタスクから削除
data.tasks.splice(taskIndex, 1);
data.updatedAt = new Date().toISOString();
}
// .tasks.json を更新(完了したタスクは除外される)
fs.writeFileSync(tasksFile, JSON.stringify(data, null, 2));
// 進捗の計算(履歴も含めて計算)
const history = loadHistory(historyFile);
const currentFeature = history.features.find(f => f.feature === data.feature && !f.completedAt);
const completedCount = currentFeature ? currentFeature.tasks.length : 0;
const activeCount = data.tasks.length;
const totalTasks = completedCount + activeCount;
if (totalTasks > 0) {
const progress = Math.round((completedCount / totalTasks) * 100);
console.log(`\n📊 全体の進捗: ${completedCount}/${totalTasks} (${progress}%)`);
}
// すべてのタスクが完了した場合
if (data.tasks.length === 0 && currentFeature) {
currentFeature.completedAt = new Date().toISOString();
fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
fs.unlinkSync(tasksFile);
console.log('\n✅ すべてのタスクが完了しました!機能開発が完了しました。');
}
} catch (error) {
console.log(`\n⚠️ タスク状態の更新に失敗しました: ${error.message}`);
}
}
// 履歴ファイルの読み込み
function loadHistory(historyFile) {
if (!fs.existsSync(historyFile)) {
return { features: [] };
}
return JSON.parse(fs.readFileSync(historyFile, 'utf-8'));
}
// タスクを履歴に移動
function moveTaskToHistory(featureName, task, historyFile) {
const history = loadHistory(historyFile);
// 現在の機能を検索(まだ完了していないもの)
let currentFeature = history.features.find(f => f.feature === featureName && !f.completedAt);
if (!currentFeature) {
// 新しい機能エントリを作成
currentFeature = {
feature: featureName,
createdAt: new Date().toISOString(),
tasks: []
};
history.features.push(currentFeature);
}
// タスクを履歴に追加
currentFeature.tasks.push({
id: task.id,
type: task.type,
name: task.name,
status: task.status,
command: task.command,
parent: task.parent,
dependencies: task.dependencies,
createdAt: task.createdAt,
startedAt: task.startedAt,
completedAt: task.completedAt
});
// 履歴ファイルに書き込み
fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
}
// エラーハンドリング
try {
main();
} catch (error) {
console.error('❌ チェック中にエラーが発生しました:', error.message);
console.error(error.stack);
process.exit(1);
}