tdd-guard
Version:
TDD Guard enforces Test-Driven Development principles using Claude Code hooks
172 lines (165 loc) • 7.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateDynamicContext = generateDynamicContext;
const toolSchemas_1 = require("../../contracts/schemas/toolSchemas");
const processors_1 = require("../../processors");
const lintProcessor_1 = require("../../processors/lintProcessor");
const fileTypeDetection_1 = require("../../hooks/fileTypeDetection");
// Import core prompts (always included)
const role_and_context_1 = require("../prompts/role-and-context");
const tdd_core_principles_1 = require("../prompts/tdd-core-principles");
const file_type_rules_1 = require("../prompts/file-type-rules");
const response_format_1 = require("../prompts/response-format");
// Import operation-specific analysis
const edit_analysis_1 = require("../prompts/edit-analysis");
const multi_edit_analysis_1 = require("../prompts/multi-edit-analysis");
const write_analysis_1 = require("../prompts/write-analysis");
function generateDynamicContext(context) {
const operation = JSON.parse(context.modifications);
// Build prompt in correct order
const sections = [
// 1. Core sections (always included)
role_and_context_1.ROLE_AND_CONTEXT,
tdd_core_principles_1.TDD_CORE_PRINCIPLES,
file_type_rules_1.FILE_TYPE_RULES,
// 2. Operation-specific analysis (only for current operation)
getOperationAnalysis(operation),
// 3. Changes under review
'\n## Changes to Review\n',
formatOperation(operation),
// 4. Additional context
formatTestOutput(context.test ?? '', operation),
context.todo ? formatTodoList(context.todo) : '',
context.lint ? formatLintOutput(context.lint) : '',
// 5. Response format
response_format_1.RESPONSE_FORMAT,
];
return sections.filter(Boolean).join('\n');
}
function getOperationAnalysis(operation) {
if ((0, toolSchemas_1.isEditOperation)(operation)) {
return edit_analysis_1.EDIT_ANALYSIS;
}
if ((0, toolSchemas_1.isMultiEditOperation)(operation)) {
return multi_edit_analysis_1.MULTI_EDIT_ANALYSIS;
}
if ((0, toolSchemas_1.isWriteOperation)(operation)) {
return write_analysis_1.WRITE_ANALYSIS;
}
return '';
}
function formatOperation(operation) {
if ((0, toolSchemas_1.isEditOperation)(operation)) {
return formatEditOperation(operation);
}
if ((0, toolSchemas_1.isMultiEditOperation)(operation)) {
return formatMultiEditOperation(operation);
}
if ((0, toolSchemas_1.isWriteOperation)(operation)) {
return formatWriteOperation(operation);
}
return '';
}
// Context section descriptions
const EDIT_MODIFICATIONS_DESCRIPTION = `This section shows the code changes being proposed. Compare the old content with the new content to identify what's being added, removed, or modified.`;
const WRITE_MODIFICATIONS_DESCRIPTION = `This section shows the new file being created. Analyze the content to determine if it follows TDD principles for new file creation.`;
const TEST_OUTPUT_DESCRIPTION = `This section shows the output from the most recent test run BEFORE this modification.
IMPORTANT: This test output is from PREVIOUS work, not from the changes being reviewed. The modification has NOT been executed yet.
Use this to understand:
- Which tests are failing and why (from previous work)
- What error messages indicate about missing implementation
- Whether tests are passing (indicating refactor phase may be appropriate)
Note: Test output may be from unrelated features. This does NOT prevent starting new test-driven work.`;
const TODO_LIST_DESCRIPTION = `This section shows the developer's task list. Use this to understand:
- What the developer is currently working on (in_progress)
- What has been completed (completed)
- What is planned next (pending)
Note: Multiple pending "add test" todos don't justify adding multiple tests at once.`;
const LINT_OUTPUT_DESCRIPTION = `This section shows the current code quality status from static analysis.
IMPORTANT: This lint output reflects the CURRENT state of the codebase BEFORE the proposed modification.
Use this to understand:
- Current code quality issues that need attention
- Whether code quality should be addressed before new features
- Patterns of issues that may indicate architectural concerns
Note: During TDD red phase (failing tests), focus on making tests pass before addressing lint issues.
During green phase (passing tests), lint issues should be addressed before proceeding to new features.`;
function formatEditOperation(operation) {
return [
EDIT_MODIFICATIONS_DESCRIPTION,
formatSection('File Path', operation.tool_input.file_path),
formatSection('Old Content', operation.tool_input.old_string),
formatSection('New Content', operation.tool_input.new_string),
].join('\n');
}
function formatMultiEditOperation(operation) {
const editsFormatted = operation.tool_input.edits
.map((edit, index) => formatEdit(edit, index + 1))
.join('');
return [
EDIT_MODIFICATIONS_DESCRIPTION,
formatSection('File Path', operation.tool_input.file_path),
'\n### Edits\n',
editsFormatted,
].join('\n');
}
function formatWriteOperation(operation) {
return [
WRITE_MODIFICATIONS_DESCRIPTION,
formatSection('File Path', operation.tool_input.file_path),
formatSection('New File Content', operation.tool_input.content),
].join('\n');
}
function formatEdit(edit, index) {
const fenceMarker = '```';
return [
`\n#### Edit ${index}:\n`,
`**Old Content:**\n${fenceMarker}\n${edit.old_string}\n${fenceMarker}\n`,
`**New Content:**\n${fenceMarker}\n${edit.new_string}\n${fenceMarker}\n`,
].join('');
}
function formatTestOutput(testOutput, operation) {
// Handle empty or missing test output
if (!testOutput || testOutput.trim() === '') {
return [
'\n### Test Output\n',
TEST_OUTPUT_DESCRIPTION,
'\n```\n',
'No test output available. Tests must be run before implementing.',
'\n```\n',
].join('');
}
const processor = new processors_1.TestResultsProcessor();
// Use existing file type detection
const fileType = (0, fileTypeDetection_1.detectFileType)({ tool_input: operation.tool_input });
const framework = fileType === 'python' ? 'pytest' : 'vitest';
const formattedOutput = processor.process(testOutput, framework);
return [
'\n### Test Output\n',
TEST_OUTPUT_DESCRIPTION,
'\n```\n',
formattedOutput,
'\n```\n',
].join('');
}
function formatTodoList(todoJson) {
const todoOperation = JSON.parse(todoJson);
const todos = todoOperation.tool_input?.todos ?? [];
const todoItems = todos
.map((todo, index) => `\n${index + 1}. [${todo.status}] ${todo.content} (${todo.priority})`)
.join('');
return ['\n### Todo List\n', TODO_LIST_DESCRIPTION, todoItems, '\n'].join('');
}
function formatLintOutput(lintData) {
const formattedLintData = (0, lintProcessor_1.formatLintDataForContext)(lintData);
return [
'\n### Code Quality Status\n',
LINT_OUTPUT_DESCRIPTION,
'\n```\n',
formattedLintData,
'\n```\n',
].join('');
}
function formatSection(title, content) {
const fenceMarker = '```';
return `\n### ${title}\n${fenceMarker}\n${content}\n${fenceMarker}\n`;
}