feat: implement tddai Python library for TDD workspace management
- Create comprehensive tddai package with workspace, issue fetcher, and test generator modules - Add Python CLI interface (tddai_cli.py) to replace complex Makefile shell logic - Update Makefile targets to use Python CLI for better maintainability - Implement proper behavior-based tests instead of file existence checks - Add workspace lifecycle management (create, active, finish, cleanup) - Add issue fetching from Gitea API with error handling - Add comprehensive test coverage with 19 passing tests - Support environment variable configuration for different deployments This addresses issue #11: Setup TDD workspace infrastructure All tests pass and the system achieves green state before commit. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
183
Makefile
183
Makefile
@@ -230,22 +230,7 @@ CURRENT_ISSUE_FILE := $(WORKSPACE_DIR)/current_issue.json
|
||||
|
||||
# List all gitea issues
|
||||
list-issues:
|
||||
@echo "📋 MarkiTect Issues from Gitea Repository"
|
||||
@echo "========================================"
|
||||
@echo ""
|
||||
@if ! command -v curl >/dev/null 2>&1; then \
|
||||
echo "❌ curl not found - required for API access"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v jq >/dev/null 2>&1; then \
|
||||
echo "⚠️ jq not found - using basic formatting"; \
|
||||
echo " Install jq for better formatting: sudo apt install jq"; \
|
||||
curl -s "$(ISSUES_API)" | head -20; \
|
||||
else \
|
||||
curl -s "$(ISSUES_API)" | jq -r '.[] | "[\(.state | ascii_upcase)] #\(.number): \(.title)\n Created: \(.created_at[:10]) | Updated: \(.updated_at[:10])\n \(.body[:80])...\n"' | head -40; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "💡 Tip: Use 'make show-issue NUM=X' to see full details"
|
||||
@python3 tddai_cli.py list-issues
|
||||
|
||||
# Show detailed view of a specific issue
|
||||
show-issue:
|
||||
@@ -253,39 +238,11 @@ show-issue:
|
||||
echo "❌ Please specify issue number: make show-issue NUM=5"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v curl >/dev/null 2>&1; then \
|
||||
echo "❌ curl not found - required for API access"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "🔍 Issue #$(NUM) Details"
|
||||
@echo "======================="
|
||||
@echo ""
|
||||
@if ! command -v jq >/dev/null 2>&1; then \
|
||||
echo "⚠️ jq not found - using basic formatting"; \
|
||||
curl -s "$(ISSUES_API)/$(NUM)"; \
|
||||
else \
|
||||
curl -s "$(ISSUES_API)/$(NUM)" | jq -r 'if . == null or .message then "❌ Issue #$(NUM) not found or API error" else "**Title:** " + .title + "\n**Status:** " + (.state | ascii_upcase) + "\n**Number:** #" + (.number | tostring) + "\n**Created:** " + (.created_at[:10]) + " by " + (.user.full_name // .user.login) + "\n**Updated:** " + (.updated_at[:10]) + "\n**URL:** " + .html_url + "\n\n**Description:**\n" + .body end' 2>/dev/null || echo "❌ Issue #$(NUM) not found or API error"; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "💡 Tip: Use 'make list-issues' to see all issues"
|
||||
@python3 tddai_cli.py show-issue $(NUM)
|
||||
|
||||
# List only open issues (active backlog)
|
||||
list-open-issues:
|
||||
@echo "📋 Open MarkiTect Issues (Active Backlog)"
|
||||
@echo "========================================"
|
||||
@echo ""
|
||||
@if ! command -v curl >/dev/null 2>&1; then \
|
||||
echo "❌ curl not found - required for API access"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v jq >/dev/null 2>&1; then \
|
||||
echo "⚠️ jq not found - using basic formatting"; \
|
||||
curl -s "$(ISSUES_API)?state=open" | head -20; \
|
||||
else \
|
||||
curl -s "$(ISSUES_API)?state=open" | jq -r '.[] | "[OPEN] #\(.number): \(.title)\n Created: \(.created_at[:10]) | Updated: \(.updated_at[:10])\n \(.body[:80])...\n"' | head -40; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "💡 Tip: Use 'make show-issue NUM=X' for full details or 'make list-issues' for all issues"
|
||||
@python3 tddai_cli.py list-open-issues
|
||||
|
||||
# Generate test skeleton from gitea issue (requires Claude Code)
|
||||
test-from-issue:
|
||||
@@ -316,142 +273,16 @@ start-issue:
|
||||
echo "❌ Please specify issue number: make start-issue NUM=1"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "🔍 Starting work on issue #$(NUM)..."
|
||||
@if [ -f "$(CURRENT_ISSUE_FILE)" ]; then \
|
||||
CURRENT=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.number // "unknown"'); \
|
||||
echo "⚠️ Already working on issue #$$CURRENT"; \
|
||||
echo " Run 'make finish-issue' first or 'make workspace-status' to see details"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then \
|
||||
echo "❌ curl and jq required for workspace management"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "📋 Fetching issue #$(NUM) details..."
|
||||
@ISSUE_DATA=$$(curl -s "$(ISSUES_API)/$(NUM)" 2>/dev/null); \
|
||||
if echo "$$ISSUE_DATA" | jq -e '.title' >/dev/null 2>&1; then \
|
||||
mkdir -p "$(WORKSPACE_DIR)/issue_$(NUM)/tests"; \
|
||||
echo "$$ISSUE_DATA" | jq '{number: .number, title: .title, body: .body, state: .state, created_at: .created_at, html_url: .html_url}' > "$(CURRENT_ISSUE_FILE)"; \
|
||||
echo "$$ISSUE_DATA" | jq -r '"# Issue #" + (.number | tostring) + ": " + .title + "\n\n## Description\n" + .body + "\n\n## Requirements Breakdown\n\n- [ ] TODO: Break down requirements into testable scenarios\n- [ ] TODO: Identify edge cases\n- [ ] TODO: Define acceptance criteria\n\n## Test Plan\n\n- [ ] TODO: List specific test scenarios to implement\n"' > "$(WORKSPACE_DIR)/issue_$(NUM)/requirements.md"; \
|
||||
echo "# Test Plan for Issue #$(NUM)" > "$(WORKSPACE_DIR)/issue_$(NUM)/test_plan.md"; \
|
||||
echo "" >> "$(WORKSPACE_DIR)/issue_$(NUM)/test_plan.md"; \
|
||||
echo "## Test Scenarios" >> "$(WORKSPACE_DIR)/issue_$(NUM)/test_plan.md"; \
|
||||
echo "" >> "$(WORKSPACE_DIR)/issue_$(NUM)/test_plan.md"; \
|
||||
echo "- [ ] TODO: Add specific test scenarios" >> "$(WORKSPACE_DIR)/issue_$(NUM)/test_plan.md"; \
|
||||
echo "✅ Workspace created for issue #$(NUM)"; \
|
||||
echo "📁 Workspace: $(WORKSPACE_DIR)/issue_$(NUM)/"; \
|
||||
echo "📋 Requirements: $(WORKSPACE_DIR)/issue_$(NUM)/requirements.md"; \
|
||||
echo "🧪 Test plan: $(WORKSPACE_DIR)/issue_$(NUM)/test_plan.md"; \
|
||||
echo ""; \
|
||||
echo "💡 Next steps:"; \
|
||||
echo " 1. Review requirements.md and break down the issue"; \
|
||||
echo " 2. Plan test scenarios in test_plan.md"; \
|
||||
echo " 3. Use 'make add-test' to generate tests"; \
|
||||
echo " 4. Use 'make finish-issue' when complete"; \
|
||||
else \
|
||||
echo "❌ Issue #$(NUM) not found or API error"; \
|
||||
echo " Use 'make list-open-issues' to see available issues"; \
|
||||
fi
|
||||
@python3 tddai_cli.py start-issue $(NUM)
|
||||
|
||||
# Add test to current issue workspace
|
||||
add-test:
|
||||
@if [ ! -f "$(CURRENT_ISSUE_FILE)" ]; then \
|
||||
echo "❌ No active issue workspace"; \
|
||||
echo " Run 'make start-issue NUM=X' first"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v claude >/dev/null 2>&1; then \
|
||||
echo "❌ Claude Code not found - required for test generation"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@CURRENT_ISSUE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.number'); \
|
||||
ISSUE_TITLE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.title'); \
|
||||
ISSUE_BODY=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.body'); \
|
||||
echo "🧪 Adding test to issue #$$CURRENT_ISSUE workspace"; \
|
||||
echo ""; \
|
||||
echo "📋 Issue: $$ISSUE_TITLE"; \
|
||||
echo "📁 Workspace: $(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/"; \
|
||||
echo ""; \
|
||||
echo "🤖 Please ask Claude Code to generate a test:"; \
|
||||
echo ""; \
|
||||
echo " Command: 'Generate a test for the current workspace issue'"; \
|
||||
echo ""; \
|
||||
echo "📝 Test Requirements:"; \
|
||||
echo " - Save test in: $(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests/"; \
|
||||
echo " - Name format: test_issue_$$CURRENT_ISSUE_<scenario>.py"; \
|
||||
echo " - Include docstring referencing issue #$$CURRENT_ISSUE"; \
|
||||
echo " - Follow TDD principles (test should fail initially)"; \
|
||||
echo " - Review requirements.md and test_plan.md for context"; \
|
||||
echo ""; \
|
||||
echo "📋 Issue Details:"; \
|
||||
echo " Title: $$ISSUE_TITLE"; \
|
||||
echo " Description: $$ISSUE_BODY"; \
|
||||
echo ""; \
|
||||
echo "💡 After generation: Use 'make workspace-status' to see all tests"
|
||||
@python3 tddai_cli.py add-test
|
||||
|
||||
# Show current workspace status
|
||||
workspace-status:
|
||||
@if [ ! -f "$(CURRENT_ISSUE_FILE)" ]; then \
|
||||
echo "📋 No active issue workspace"; \
|
||||
echo " Use 'make start-issue NUM=X' to begin working on an issue"; \
|
||||
exit 0; \
|
||||
fi
|
||||
@CURRENT_ISSUE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.number'); \
|
||||
ISSUE_TITLE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.title'); \
|
||||
ISSUE_STATE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.state'); \
|
||||
echo "📋 Active Issue Workspace"; \
|
||||
echo "========================"; \
|
||||
echo ""; \
|
||||
echo "🎯 Issue #$$CURRENT_ISSUE: $$ISSUE_TITLE"; \
|
||||
echo "📊 Status: $$ISSUE_STATE"; \
|
||||
echo "📁 Workspace: $(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/"; \
|
||||
echo ""; \
|
||||
if [ -d "$(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests" ]; then \
|
||||
TEST_COUNT=$$(find "$(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests" -name "*.py" | wc -l); \
|
||||
echo "🧪 Generated Tests ($$TEST_COUNT):"; \
|
||||
if [ $$TEST_COUNT -gt 0 ]; then \
|
||||
find "$(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests" -name "*.py" -exec basename {} \; | sed 's/^/ - /'; \
|
||||
else \
|
||||
echo " - No tests generated yet"; \
|
||||
fi; \
|
||||
echo ""; \
|
||||
fi; \
|
||||
echo "📋 Workspace Files:"; \
|
||||
echo " - requirements.md (review and break down issue)"; \
|
||||
echo " - test_plan.md (plan test scenarios)"; \
|
||||
echo " - tests/ (generated test files)"; \
|
||||
echo ""; \
|
||||
echo "💡 Commands:"; \
|
||||
echo " - make add-test (generate another test)"; \
|
||||
echo " - make finish-issue (complete and move tests to main)"
|
||||
@python3 tddai_cli.py workspace-status
|
||||
|
||||
# Complete issue work (move tests to main and cleanup)
|
||||
finish-issue:
|
||||
@if [ ! -f "$(CURRENT_ISSUE_FILE)" ]; then \
|
||||
echo "❌ No active issue workspace"; \
|
||||
echo " Nothing to finish"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@CURRENT_ISSUE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.number'); \
|
||||
ISSUE_TITLE=$$(cat "$(CURRENT_ISSUE_FILE)" | jq -r '.title'); \
|
||||
echo "🏁 Finishing work on issue #$$CURRENT_ISSUE"; \
|
||||
echo ""; \
|
||||
if [ -d "$(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests" ]; then \
|
||||
TEST_COUNT=$$(find "$(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests" -name "*.py" | wc -l); \
|
||||
if [ $$TEST_COUNT -gt 0 ]; then \
|
||||
echo "📦 Moving $$TEST_COUNT test(s) to tests/ directory..."; \
|
||||
cp $(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE/tests/*.py tests/ 2>/dev/null || echo " No .py files to move"; \
|
||||
echo "✅ Tests moved to main tests/ directory"; \
|
||||
else \
|
||||
echo "⚠️ No tests found in workspace"; \
|
||||
fi; \
|
||||
fi; \
|
||||
echo "🧹 Cleaning up workspace..."; \
|
||||
rm -rf "$(WORKSPACE_DIR)/issue_$$CURRENT_ISSUE"; \
|
||||
rm -f "$(CURRENT_ISSUE_FILE)"; \
|
||||
echo "✅ Issue #$$CURRENT_ISSUE workspace cleaned up"; \
|
||||
echo ""; \
|
||||
echo "💡 Next steps:"; \
|
||||
echo " - Run 'make test' to verify tests fail (red state)"; \
|
||||
echo " - Implement code to make tests pass (green state)"; \
|
||||
echo " - Start next issue with 'make start-issue NUM=X'"
|
||||
@python3 tddai_cli.py finish-issue
|
||||
|
||||
Reference in New Issue
Block a user