182 lines
5.2 KiB
JavaScript
182 lines
5.2 KiB
JavaScript
#!/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);
|
||
}
|