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

181
hooks/post-docs-check.js Normal file
View 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);
}