Initial commit
This commit is contained in:
38
hooks/hooks.json
Normal file
38
hooks/hooks.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "SlashCommand",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/post-docs-check.js",
|
||||
"description": "ドキュメント作成後の品質チェック",
|
||||
"when": {
|
||||
"field": "tool_input.command",
|
||||
"equals": "/create-docs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/post-test-check.js",
|
||||
"description": "テスト作成後の品質チェック",
|
||||
"when": {
|
||||
"field": "tool_input.command",
|
||||
"equals": "/create-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/post-implementation.js",
|
||||
"description": "実装後の品質チェックとテスト実行",
|
||||
"when": {
|
||||
"field": "tool_input.command",
|
||||
"equals": "/implement"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
181
hooks/post-docs-check.js
Normal file
181
hooks/post-docs-check.js
Normal file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ドキュメント作成後のチェックフック
|
||||
*
|
||||
* このフックは /create-docs コマンド実行後に自動的に実行され、
|
||||
* 作成されたドキュメントの完全性と品質をチェックします。
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// ドキュメントの必須セクション
|
||||
const REQUIRED_SECTIONS = [
|
||||
'概要',
|
||||
'機能要件',
|
||||
'技術仕様',
|
||||
'非機能要件',
|
||||
'テスト',
|
||||
];
|
||||
|
||||
// ドキュメントファイルを探す
|
||||
function findDocFiles() {
|
||||
const possiblePaths = [
|
||||
'docs/features',
|
||||
'docs/specs',
|
||||
'docs',
|
||||
'.',
|
||||
];
|
||||
|
||||
const docFiles = [];
|
||||
for (const dir of possiblePaths) {
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
|
||||
const files = fs.readdirSync(dir)
|
||||
.filter(f => f.endsWith('.md') && !f.startsWith('README'))
|
||||
.map(f => path.join(dir, f));
|
||||
|
||||
docFiles.push(...files);
|
||||
}
|
||||
|
||||
return docFiles;
|
||||
}
|
||||
|
||||
// ドキュメントの内容をチェック
|
||||
function checkDocument(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const issues = [];
|
||||
|
||||
// 必須セクションのチェック
|
||||
const missingSection = REQUIRED_SECTIONS.filter(section => {
|
||||
const regex = new RegExp(`##?\\s+${section}`, 'i');
|
||||
return !regex.test(content);
|
||||
});
|
||||
|
||||
if (missingSections.length > 0) {
|
||||
issues.push({
|
||||
type: 'missing_sections',
|
||||
message: `必須セクションが不足しています: ${missingSections.join(', ')}`,
|
||||
});
|
||||
}
|
||||
|
||||
// 最小文字数チェック(簡易的な品質チェック)
|
||||
if (content.length < 500) {
|
||||
issues.push({
|
||||
type: 'too_short',
|
||||
message: 'ドキュメントが短すぎます。より詳細な説明が必要です。',
|
||||
});
|
||||
}
|
||||
|
||||
// TODOやFIXMEが残っていないかチェック
|
||||
const todoMatches = content.match(/TODO|FIXME|XXX/g);
|
||||
if (todoMatches && todoMatches.length > 0) {
|
||||
issues.push({
|
||||
type: 'incomplete',
|
||||
message: `未完了の項目が ${todoMatches.length} 件あります(TODO/FIXME)`,
|
||||
});
|
||||
}
|
||||
|
||||
// コードブロックがあるかチェック(技術仕様には必要)
|
||||
if (!content.includes('```')) {
|
||||
issues.push({
|
||||
type: 'no_code_examples',
|
||||
message: 'コード例やデータ構造の記載がありません',
|
||||
severity: 'warning',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
filePath,
|
||||
issues,
|
||||
passed: issues.filter(i => i.severity !== 'warning').length === 0,
|
||||
};
|
||||
}
|
||||
|
||||
// メイン処理
|
||||
function main() {
|
||||
console.log('📄 ドキュメントの品質チェックを実行中...\n');
|
||||
|
||||
const docFiles = findDocFiles();
|
||||
|
||||
if (docFiles.length === 0) {
|
||||
console.error('❌ ドキュメントファイルが見つかりません');
|
||||
console.error(' 以下のディレクトリにMarkdownファイルを作成してください:');
|
||||
console.error(' - docs/features/');
|
||||
console.error(' - docs/specs/');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`検出されたドキュメント: ${docFiles.length}件\n`);
|
||||
|
||||
const results = docFiles.map(checkDocument);
|
||||
const failedDocs = results.filter(r => !r.passed);
|
||||
|
||||
// 結果の表示
|
||||
results.forEach(result => {
|
||||
if (result.passed) {
|
||||
console.log(`✅ ${result.filePath}`);
|
||||
} else {
|
||||
console.log(`❌ ${result.filePath}`);
|
||||
result.issues.forEach(issue => {
|
||||
const icon = issue.severity === 'warning' ? '⚠️' : '❌';
|
||||
console.log(` ${icon} ${issue.message}`);
|
||||
});
|
||||
}
|
||||
console.log();
|
||||
});
|
||||
|
||||
// サマリー
|
||||
console.log('─'.repeat(50));
|
||||
console.log(`合計: ${results.length}件`);
|
||||
console.log(`成功: ${results.length - failedDocs.length}件`);
|
||||
console.log(`失敗: ${failedDocs.length}件`);
|
||||
|
||||
if (failedDocs.length > 0) {
|
||||
console.log('\n⚠️ ドキュメントに問題があります。修正してから次のステップに進んでください。');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('\n✅ ドキュメントの品質チェックが完了しました!');
|
||||
console.log(' 次のステップ: /create-tests を実行してテストケースを作成');
|
||||
|
||||
// タスク状態の自動更新
|
||||
updateTaskStatus(2, 'completed');
|
||||
}
|
||||
|
||||
// タスク状態を更新する関数
|
||||
function updateTaskStatus(taskId, status) {
|
||||
const tasksFile = '.tasks.json';
|
||||
|
||||
if (!fs.existsSync(tasksFile)) {
|
||||
console.log('\n⚠️ タスクファイルが見つかりません。タスク状態は更新されませんでした。');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(tasksFile, 'utf-8'));
|
||||
const task = data.tasks.find(t => t.id === taskId);
|
||||
|
||||
if (!task) {
|
||||
console.log(`\n⚠️ タスク #${taskId} が見つかりません。`);
|
||||
return;
|
||||
}
|
||||
|
||||
task.status = status;
|
||||
task.completedAt = new Date().toISOString();
|
||||
data.updatedAt = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(tasksFile, JSON.stringify(data, null, 2));
|
||||
console.log(`\n📋 タスク #${taskId} (${task.name}) を完了としてマークしました`);
|
||||
} catch (error) {
|
||||
console.log(`\n⚠️ タスク状態の更新に失敗しました: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// エラーハンドリング
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error('❌ チェック中にエラーが発生しました:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
273
hooks/post-implementation.js
Normal file
273
hooks/post-implementation.js
Normal 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);
|
||||
}
|
||||
158
hooks/post-test-check.js
Normal file
158
hooks/post-test-check.js
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* テスト作成後のチェックフック
|
||||
*
|
||||
* このフックは /create-tests コマンド実行後に自動的に実行され、
|
||||
* 作成されたテストを実行します。
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
|
||||
// テストを実行
|
||||
function runTests() {
|
||||
console.log('🧪 テストを実行中...\n');
|
||||
|
||||
try {
|
||||
// package.json の存在確認
|
||||
if (fs.existsSync('package.json')) {
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
||||
if (packageJson.scripts && packageJson.scripts.test) {
|
||||
const output = execSync('npm test', { encoding: 'utf-8', stdio: 'pipe' });
|
||||
console.log(output);
|
||||
return { success: true, output };
|
||||
}
|
||||
}
|
||||
|
||||
// pytest の確認
|
||||
if (fs.existsSync('pytest.ini') || fs.existsSync('setup.py')) {
|
||||
const output = execSync('pytest', { encoding: 'utf-8', stdio: 'pipe' });
|
||||
console.log(output);
|
||||
return { success: true, output };
|
||||
}
|
||||
|
||||
console.log('⚠️ テスト実行コマンドが見つかりません');
|
||||
return { success: false, output: '' };
|
||||
} catch (error) {
|
||||
// テストが失敗するのは期待通り(TDD)
|
||||
console.log('❌ テストが失敗しました(TDDでは正常)\n');
|
||||
console.log(error.stdout || error.message);
|
||||
return { success: false, output: error.stdout || error.message, expected: true };
|
||||
}
|
||||
}
|
||||
|
||||
// メイン処理
|
||||
function main() {
|
||||
console.log('🧪 テストを実行中...\n');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
// テストの実行
|
||||
const testResult = runTests();
|
||||
|
||||
if (testResult.expected) {
|
||||
console.log('\n📝 TDDのフロー:');
|
||||
console.log(' 1. テストが失敗している(Red) ← 今ここ');
|
||||
console.log(' 2. /implement を実行して実装する');
|
||||
console.log(' 3. テストが成功する(Green)');
|
||||
console.log(' 4. リファクタリング');
|
||||
}
|
||||
|
||||
console.log('\n次のステップ: /implement を実行して機能を実装');
|
||||
|
||||
// タスク状態の自動更新
|
||||
updateTaskStatus(3, '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));
|
||||
} 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);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user