BMAD-METHOD/tools/claude-code-hooks/context-loader.js

80 lines
2.5 KiB
JavaScript

#!/usr/bin/env node
/**
* BMAD Context Loader Hook
* Automatically loads active story context and quality reminders
* Runs on UserPromptSubmit to enhance prompts with relevant context
*/
const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
async function loadActiveContext() {
try {
// Find active story files
const storyFiles = glob.sync('**/STORY-*.md', {
ignore: ['node_modules/**', '.git/**']
});
// Find the most recently modified story
let activeStory = null;
let latestTime = 0;
for (const file of storyFiles) {
const stats = await fs.stat(file);
const content = await fs.readFile(file, 'utf8');
// Check if story is in progress
if (content.includes('Status: In Progress') && stats.mtimeMs > latestTime) {
activeStory = file;
latestTime = stats.mtimeMs;
}
}
const context = {
approve: true,
messages: []
};
if (activeStory) {
const storyContent = await fs.readFile(activeStory, 'utf8');
// Extract key sections
const acceptanceCriteria = extractSection(storyContent, 'Acceptance Criteria');
const implementationNotes = extractSection(storyContent, 'Implementation Notes');
if (acceptanceCriteria || implementationNotes) {
context.messages.push({
role: 'system',
content: `Active Story Context from ${path.basename(activeStory)}:\n\n` +
(acceptanceCriteria ? `Acceptance Criteria:\n${acceptanceCriteria}\n\n` : '') +
(implementationNotes ? `Implementation Notes:\n${implementationNotes}` : '')
});
}
}
// Add quality reminders if working on implementation
const prompt = process.env.CLAUDE_CODE_PROMPT || '';
if (prompt.includes('implement') || prompt.includes('develop') || prompt.includes('fix')) {
context.messages.push({
role: 'system',
content: 'BMAD Quality Reminder: Ensure all implementations are complete and functional. ' +
'No stubs, mocks, or placeholder code. Use *reality-audit for validation.'
});
}
console.log(JSON.stringify(context));
} catch (error) {
// On error, approve but don't add context
console.log(JSON.stringify({ approve: true }));
}
}
function extractSection(content, sectionName) {
const regex = new RegExp(`### ${sectionName}\\s*([\\s\\S]*?)(?=###|$)`, 'i');
const match = content.match(regex);
return match ? match[1].trim() : null;
}
loadActiveContext();