From 90f3597587fe31892151ff3c35db1cca4cf68a29 Mon Sep 17 00:00:00 2001 From: mazemaze Date: Fri, 19 Dec 2025 06:21:53 +0900 Subject: [PATCH] Deploy update --- .claude/agents/backend-implementer.md | 669 +++++ .claude/agents/context-gatherer.md | 261 ++ .claude/agents/deployer.md | 366 +++ .claude/agents/frontend-implementer.md | 707 +++++ .claude/agents/stripe-payment-implementer.md | 487 ++++ .claude/agents/type-generator.md | 229 ++ .claude/agents/workflow-orchestrator.md | 315 +++ .claude/agents/workflow-reviewer.md | 96 + .claude/agents/workflow-validator.md | 152 ++ .claude/commands/eureka/deploy-flow.md | 70 + .claude/commands/eureka/deploy-logs.md | 63 + .claude/commands/eureka/deploy-status.md | 55 + .claude/commands/eureka/deploy.md | 421 +++ .claude/commands/eureka/index.md | 595 +++++ .claude/commands/eureka/landing.md | 660 +++++ .claude/commands/guardrail/analyze.md | 243 ++ .claude/commands/guardrail/approve.md | 47 + .claude/commands/guardrail/design.md | 81 + .claude/commands/guardrail/implement.md | 74 + .claude/commands/guardrail/init.md | 67 + .claude/commands/guardrail/review.md | 32 + .claude/commands/guardrail/status.md | 27 + .claude/commands/guardrail/validate.md | 29 + .claude/commands/guardrail/verify.md | 57 + .claude/commands/workflow/approve.md | 87 + .claude/commands/workflow/backend.md | 86 + .claude/commands/workflow/complete.md | 66 + .claude/commands/workflow/design.md | 585 +++++ .claude/commands/workflow/diff.md | 193 ++ .claude/commands/workflow/frontend.md | 86 + .claude/commands/workflow/history.md | 94 + .claude/commands/workflow/reject.md | 113 + .claude/commands/workflow/resume.md | 241 ++ .claude/commands/workflow/review.md | 569 +++++ .claude/commands/workflow/security.md | 342 +++ .claude/commands/workflow/spawn.md | 2274 +++++++++++++++++ .claude/commands/workflow/status.md | 119 + .claude/references/dockerfile-templates.md | 621 +++++ .claude/references/eureka-infrastructure.md | 237 ++ .claude/references/stripe-config.example.yml | 303 +++ .claude/references/stripe-payment-types.md | 427 ++++ .claude/settings.json | 225 ++ .gitignore | 5 + .mcp.json | 16 + .workflow/gathered_context.yml | 28 + .workflow/index.yml | 11 + .../v001/contexts/api_create_recording.yml | 160 ++ .../versions/v001/contexts/api_delete_app.yml | 158 ++ .../v001/contexts/api_delete_recording.yml | 139 + .../v001/contexts/api_generate_app.yml | 315 +++ .../versions/v001/contexts/api_get_app.yml | 170 ++ .../v001/contexts/api_get_current_user.yml | 126 + .../v001/contexts/api_get_recording.yml | 151 ++ .../versions/v001/contexts/api_list_apps.yml | 164 ++ .../v001/contexts/api_list_recordings.yml | 144 ++ .../versions/v001/contexts/api_login_user.yml | 139 + .../v001/contexts/api_logout_user.yml | 54 + .../v001/contexts/api_register_user.yml | 161 ++ .../v001/contexts/api_summarize_recording.yml | 207 ++ .../contexts/api_transcribe_recording.yml | 143 ++ .../v001/contexts/component_app_card.yml | 51 + .../v001/contexts/component_app_gallery.yml | 82 + .../contexts/component_app_iframe_viewer.yml | 48 + .../v001/contexts/component_audio_player.yml | 51 + .../v001/contexts/component_features.yml | 34 + .../v001/contexts/component_header.yml | 39 + .../versions/v001/contexts/component_hero.yml | 37 + .../v001/contexts/component_login_form.yml | 114 + .../v001/contexts/component_record_button.yml | 52 + .../contexts/component_recording_card.yml | 51 + .../contexts/component_recording_list.yml | 85 + .../v001/contexts/component_register_form.yml | 138 + .../v001/contexts/component_sidebar.yml | 42 + .../contexts/component_summary_display.yml | 43 + .../contexts/component_transcript_viewer.yml | 44 + .../component_wake_word_indicator.yml | 46 + .../v001/contexts/model_generated_app.yml | 234 ++ .../v001/contexts/model_recording.yml | 143 ++ .../versions/v001/contexts/model_user.yml | 71 + .../v001/contexts/page_app_detail.yml | 189 ++ .../versions/v001/contexts/page_apps.yml | 218 ++ .../versions/v001/contexts/page_dashboard.yml | 312 +++ .../versions/v001/contexts/page_home.yml | 103 + .../versions/v001/contexts/page_login.yml | 72 + .../v001/contexts/page_recording_detail.yml | 249 ++ .../v001/contexts/page_recordings.yml | 221 ++ .../versions/v001/contexts/page_register.yml | 74 + .../versions/v001/contracts/api_contract.yml | 907 +++++++ .workflow/versions/v001/dependency_graph.yml | 712 ++++++ .../versions/v001/design/design_document.yml | 1665 ++++++++++++ .workflow/versions/v001/relations.yml | 1175 +++++++++ .../versions/v001/requirements/summary.yml | 135 + .workflow/versions/v001/session.yml | 31 + .workflow/versions/v001/session.yml.bak | 31 + .../v001/snapshot_after/manifest.json | 321 +++ .../v001/snapshot_before/manifest.json | 16 + .../task_create_api_create_recording.yml | 19 + .../v001/tasks/task_create_api_delete_app.yml | 19 + .../task_create_api_delete_recording.yml | 19 + .../tasks/task_create_api_generate_app.yml | 21 + .../v001/tasks/task_create_api_get_app.yml | 19 + .../task_create_api_get_current_user.yml | 19 + .../tasks/task_create_api_get_recording.yml | 19 + .../v001/tasks/task_create_api_list_apps.yml | 19 + .../tasks/task_create_api_list_recordings.yml | 19 + .../v001/tasks/task_create_api_login_user.yml | 19 + .../tasks/task_create_api_logout_user.yml | 18 + .../tasks/task_create_api_register_user.yml | 19 + .../task_create_api_summarize_recording.yml | 20 + .../task_create_api_transcribe_recording.yml | 19 + .../tasks/task_create_component_app_card.yml | 18 + .../task_create_component_app_gallery.yml | 19 + ...ask_create_component_app_iframe_viewer.yml | 18 + .../task_create_component_audio_player.yml | 18 + .../tasks/task_create_component_features.yml | 18 + .../tasks/task_create_component_header.yml | 18 + .../v001/tasks/task_create_component_hero.yml | 18 + .../task_create_component_login_form.yml | 19 + .../task_create_component_record_button.yml | 18 + .../task_create_component_recording_card.yml | 18 + .../task_create_component_recording_list.yml | 19 + .../task_create_component_register_form.yml | 19 + .../tasks/task_create_component_sidebar.yml | 18 + .../task_create_component_summary_display.yml | 18 + ...ask_create_component_transcript_viewer.yml | 18 + ...k_create_component_wake_word_indicator.yml | 18 + .../tasks/task_create_model_generated_app.yml | 20 + .../tasks/task_create_model_recording.yml | 19 + .../v001/tasks/task_create_model_user.yml | 18 + .../tasks/task_create_page_app_detail.yml | 22 + .../v001/tasks/task_create_page_apps.yml | 23 + .../v001/tasks/task_create_page_dashboard.yml | 25 + .../v001/tasks/task_create_page_home.yml | 21 + .../v001/tasks/task_create_page_login.yml | 19 + .../task_create_page_recording_detail.yml | 24 + .../tasks/task_create_page_recordings.yml | 23 + .../v001/tasks/task_create_page_register.yml | 19 + CLAUDE.md | 1252 +++++++++ app/api/apps/[id]/route.ts | 102 + app/api/apps/generate/route.ts | 91 + app/api/apps/route.ts | 54 + app/api/auth/login/route.ts | 61 + app/api/auth/logout/route.ts | 19 + app/api/auth/me/route.ts | 26 + app/api/auth/register/route.ts | 68 + app/api/recordings/[id]/route.ts | 104 + app/api/recordings/[id]/summarize/route.ts | 59 + app/api/recordings/[id]/transcribe/route.ts | 52 + app/api/recordings/route.ts | 125 + app/apps/[id]/page.tsx | 115 + app/apps/page.tsx | 105 + app/components/AppCard.tsx | 69 + app/components/AppGallery.tsx | 42 + app/components/AppIframeViewer.tsx | 52 + app/components/AudioPlayer.tsx | 96 + app/components/Features.tsx | 51 + app/components/Header.tsx | 44 + app/components/Hero.tsx | 25 + app/components/LoginForm.tsx | 104 + app/components/RecordButton.tsx | 66 + app/components/RecordingCard.tsx | 65 + app/components/RecordingList.tsx | 42 + app/components/RegisterForm.tsx | 153 ++ app/components/Sidebar.tsx | 54 + app/components/SummaryDisplay.tsx | 26 + app/components/TranscriptViewer.tsx | 30 + app/components/WakeWordIndicator.tsx | 37 + app/dashboard/page.tsx | 129 + app/login/page.tsx | 20 + app/page.tsx | 77 +- app/recordings/[id]/page.tsx | 152 ++ app/recordings/page.tsx | 113 + app/register/page.tsx | 20 + app/types/api.ts | 157 ++ lib/auth.ts | 43 + lib/prisma.ts | 9 + package-lock.json | 275 +- package.json | 10 +- prisma/schema.prisma | 66 + project_manifest.json | 321 +++ .../agents/doc-writer.yml | 152 ++ .../schemas/documentation_output.yml | 274 ++ .../schemas/project_analysis.yml | 272 ++ .../scripts/analyze_project.py | 489 ++++ .../scripts/generate_html.py | 491 ++++ skills/documentation-generator/skill.yml | 97 + .../templates/documentation.html | 962 +++++++ .../agents/architect.yml | 38 + .../guardrail-orchestrator/agents/backend.yml | 239 ++ .../agents/frontend.yml | 190 ++ .../agents/integrator.yml | 291 +++ .../agents/orchestrator.yml | 85 + .../agents/reviewer.yml | 52 + .../agents/security-reviewer.md | 216 ++ .../docs/WORKFLOW_ARCHITECTURE.md | 985 +++++++ .../schemas/api_contract.yml | 347 +++ .../schemas/dependency_graph.yml | 340 +++ .../schemas/design_document.yml | 603 +++++ .../schemas/relations.yml | 370 +++ .../schemas/task_template.yml | 364 +++ .../schemas/workflow_state.yml | 116 + .../schemas/workflow_version.yml | 323 +++ .../scripts/MIGRATION_GUIDE.md | 274 ++ .../scripts/analyze_codebase.py | 486 ++++ .../scripts/build_relations.py | 745 ++++++ .../scripts/detect_framework.py | 461 ++++ .../scripts/generate_api_contract.py | 615 +++++ .../scripts/generate_types.py | 556 ++++ .../scripts/init_project.py | 73 + .../scripts/manifest_diff.py | 530 ++++ .../scripts/migrate_task_sessions.py | 265 ++ .../scripts/post_write.py | 79 + .../scripts/security_scan.py | 601 +++++ .../scripts/task_manager.py | 363 +++ .../scripts/task_state_manager.py | 715 ++++++ .../scripts/transition_phase.py | 75 + .../scripts/validate_against_contract.py | 381 +++ .../scripts/validate_api_contract.py | 536 ++++ .../scripts/validate_bash.py | 271 ++ .../scripts/validate_design.py | 1180 +++++++++ .../scripts/validate_integration.py | 522 ++++ .../scripts/validate_manifest.py | 129 + .../scripts/validate_relations.py | 576 +++++ .../scripts/validate_workflow.py | 492 ++++ .../scripts/validate_write.py | 282 ++ .../scripts/verify_async.py | 237 ++ .../scripts/verify_implementation.py | 376 +++ .../scripts/version_manager.py | 986 +++++++ .../scripts/visualize_design.py | 343 +++ .../scripts/visualize_implementation.py | 651 +++++ .../scripts/workflow_manager.py | 1150 +++++++++ .../templates/task_example.yml | 99 + .../guardrail-orchestrator/tests/__init__.py | 1 + .../tests/test_generate_types.py | 280 ++ .../tests/test_workflow_validate.py | 357 +++ start-workflow.sh | 6 + types/api-types.ts | 222 ++ types/component-props.ts | 155 ++ types/index.ts | 57 + types/types.ts | 67 + 240 files changed, 50398 insertions(+), 64 deletions(-) create mode 100644 .claude/agents/backend-implementer.md create mode 100644 .claude/agents/context-gatherer.md create mode 100644 .claude/agents/deployer.md create mode 100644 .claude/agents/frontend-implementer.md create mode 100644 .claude/agents/stripe-payment-implementer.md create mode 100644 .claude/agents/type-generator.md create mode 100644 .claude/agents/workflow-orchestrator.md create mode 100644 .claude/agents/workflow-reviewer.md create mode 100644 .claude/agents/workflow-validator.md create mode 100644 .claude/commands/eureka/deploy-flow.md create mode 100644 .claude/commands/eureka/deploy-logs.md create mode 100644 .claude/commands/eureka/deploy-status.md create mode 100644 .claude/commands/eureka/deploy.md create mode 100644 .claude/commands/eureka/index.md create mode 100644 .claude/commands/eureka/landing.md create mode 100644 .claude/commands/guardrail/analyze.md create mode 100644 .claude/commands/guardrail/approve.md create mode 100644 .claude/commands/guardrail/design.md create mode 100644 .claude/commands/guardrail/implement.md create mode 100644 .claude/commands/guardrail/init.md create mode 100644 .claude/commands/guardrail/review.md create mode 100644 .claude/commands/guardrail/status.md create mode 100644 .claude/commands/guardrail/validate.md create mode 100644 .claude/commands/guardrail/verify.md create mode 100644 .claude/commands/workflow/approve.md create mode 100644 .claude/commands/workflow/backend.md create mode 100644 .claude/commands/workflow/complete.md create mode 100644 .claude/commands/workflow/design.md create mode 100644 .claude/commands/workflow/diff.md create mode 100644 .claude/commands/workflow/frontend.md create mode 100644 .claude/commands/workflow/history.md create mode 100644 .claude/commands/workflow/reject.md create mode 100644 .claude/commands/workflow/resume.md create mode 100644 .claude/commands/workflow/review.md create mode 100644 .claude/commands/workflow/security.md create mode 100644 .claude/commands/workflow/spawn.md create mode 100644 .claude/commands/workflow/status.md create mode 100644 .claude/references/dockerfile-templates.md create mode 100644 .claude/references/eureka-infrastructure.md create mode 100644 .claude/references/stripe-config.example.yml create mode 100644 .claude/references/stripe-payment-types.md create mode 100644 .claude/settings.json create mode 100644 .mcp.json create mode 100644 .workflow/gathered_context.yml create mode 100644 .workflow/index.yml create mode 100644 .workflow/versions/v001/contexts/api_create_recording.yml create mode 100644 .workflow/versions/v001/contexts/api_delete_app.yml create mode 100644 .workflow/versions/v001/contexts/api_delete_recording.yml create mode 100644 .workflow/versions/v001/contexts/api_generate_app.yml create mode 100644 .workflow/versions/v001/contexts/api_get_app.yml create mode 100644 .workflow/versions/v001/contexts/api_get_current_user.yml create mode 100644 .workflow/versions/v001/contexts/api_get_recording.yml create mode 100644 .workflow/versions/v001/contexts/api_list_apps.yml create mode 100644 .workflow/versions/v001/contexts/api_list_recordings.yml create mode 100644 .workflow/versions/v001/contexts/api_login_user.yml create mode 100644 .workflow/versions/v001/contexts/api_logout_user.yml create mode 100644 .workflow/versions/v001/contexts/api_register_user.yml create mode 100644 .workflow/versions/v001/contexts/api_summarize_recording.yml create mode 100644 .workflow/versions/v001/contexts/api_transcribe_recording.yml create mode 100644 .workflow/versions/v001/contexts/component_app_card.yml create mode 100644 .workflow/versions/v001/contexts/component_app_gallery.yml create mode 100644 .workflow/versions/v001/contexts/component_app_iframe_viewer.yml create mode 100644 .workflow/versions/v001/contexts/component_audio_player.yml create mode 100644 .workflow/versions/v001/contexts/component_features.yml create mode 100644 .workflow/versions/v001/contexts/component_header.yml create mode 100644 .workflow/versions/v001/contexts/component_hero.yml create mode 100644 .workflow/versions/v001/contexts/component_login_form.yml create mode 100644 .workflow/versions/v001/contexts/component_record_button.yml create mode 100644 .workflow/versions/v001/contexts/component_recording_card.yml create mode 100644 .workflow/versions/v001/contexts/component_recording_list.yml create mode 100644 .workflow/versions/v001/contexts/component_register_form.yml create mode 100644 .workflow/versions/v001/contexts/component_sidebar.yml create mode 100644 .workflow/versions/v001/contexts/component_summary_display.yml create mode 100644 .workflow/versions/v001/contexts/component_transcript_viewer.yml create mode 100644 .workflow/versions/v001/contexts/component_wake_word_indicator.yml create mode 100644 .workflow/versions/v001/contexts/model_generated_app.yml create mode 100644 .workflow/versions/v001/contexts/model_recording.yml create mode 100644 .workflow/versions/v001/contexts/model_user.yml create mode 100644 .workflow/versions/v001/contexts/page_app_detail.yml create mode 100644 .workflow/versions/v001/contexts/page_apps.yml create mode 100644 .workflow/versions/v001/contexts/page_dashboard.yml create mode 100644 .workflow/versions/v001/contexts/page_home.yml create mode 100644 .workflow/versions/v001/contexts/page_login.yml create mode 100644 .workflow/versions/v001/contexts/page_recording_detail.yml create mode 100644 .workflow/versions/v001/contexts/page_recordings.yml create mode 100644 .workflow/versions/v001/contexts/page_register.yml create mode 100644 .workflow/versions/v001/contracts/api_contract.yml create mode 100644 .workflow/versions/v001/dependency_graph.yml create mode 100644 .workflow/versions/v001/design/design_document.yml create mode 100644 .workflow/versions/v001/relations.yml create mode 100644 .workflow/versions/v001/requirements/summary.yml create mode 100644 .workflow/versions/v001/session.yml create mode 100644 .workflow/versions/v001/session.yml.bak create mode 100644 .workflow/versions/v001/snapshot_after/manifest.json create mode 100644 .workflow/versions/v001/snapshot_before/manifest.json create mode 100644 .workflow/versions/v001/tasks/task_create_api_create_recording.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_delete_app.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_delete_recording.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_generate_app.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_get_app.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_get_current_user.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_get_recording.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_list_apps.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_list_recordings.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_login_user.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_logout_user.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_register_user.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_summarize_recording.yml create mode 100644 .workflow/versions/v001/tasks/task_create_api_transcribe_recording.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_app_card.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_app_gallery.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_app_iframe_viewer.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_audio_player.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_features.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_header.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_hero.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_login_form.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_record_button.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_recording_card.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_recording_list.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_register_form.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_sidebar.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_summary_display.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_transcript_viewer.yml create mode 100644 .workflow/versions/v001/tasks/task_create_component_wake_word_indicator.yml create mode 100644 .workflow/versions/v001/tasks/task_create_model_generated_app.yml create mode 100644 .workflow/versions/v001/tasks/task_create_model_recording.yml create mode 100644 .workflow/versions/v001/tasks/task_create_model_user.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_app_detail.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_apps.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_dashboard.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_home.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_login.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_recording_detail.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_recordings.yml create mode 100644 .workflow/versions/v001/tasks/task_create_page_register.yml create mode 100644 CLAUDE.md create mode 100644 app/api/apps/[id]/route.ts create mode 100644 app/api/apps/generate/route.ts create mode 100644 app/api/apps/route.ts create mode 100644 app/api/auth/login/route.ts create mode 100644 app/api/auth/logout/route.ts create mode 100644 app/api/auth/me/route.ts create mode 100644 app/api/auth/register/route.ts create mode 100644 app/api/recordings/[id]/route.ts create mode 100644 app/api/recordings/[id]/summarize/route.ts create mode 100644 app/api/recordings/[id]/transcribe/route.ts create mode 100644 app/api/recordings/route.ts create mode 100644 app/apps/[id]/page.tsx create mode 100644 app/apps/page.tsx create mode 100644 app/components/AppCard.tsx create mode 100644 app/components/AppGallery.tsx create mode 100644 app/components/AppIframeViewer.tsx create mode 100644 app/components/AudioPlayer.tsx create mode 100644 app/components/Features.tsx create mode 100644 app/components/Header.tsx create mode 100644 app/components/Hero.tsx create mode 100644 app/components/LoginForm.tsx create mode 100644 app/components/RecordButton.tsx create mode 100644 app/components/RecordingCard.tsx create mode 100644 app/components/RecordingList.tsx create mode 100644 app/components/RegisterForm.tsx create mode 100644 app/components/Sidebar.tsx create mode 100644 app/components/SummaryDisplay.tsx create mode 100644 app/components/TranscriptViewer.tsx create mode 100644 app/components/WakeWordIndicator.tsx create mode 100644 app/dashboard/page.tsx create mode 100644 app/login/page.tsx create mode 100644 app/recordings/[id]/page.tsx create mode 100644 app/recordings/page.tsx create mode 100644 app/register/page.tsx create mode 100644 app/types/api.ts create mode 100644 lib/auth.ts create mode 100644 lib/prisma.ts create mode 100644 prisma/schema.prisma create mode 100644 project_manifest.json create mode 100644 skills/documentation-generator/agents/doc-writer.yml create mode 100644 skills/documentation-generator/schemas/documentation_output.yml create mode 100644 skills/documentation-generator/schemas/project_analysis.yml create mode 100644 skills/documentation-generator/scripts/analyze_project.py create mode 100644 skills/documentation-generator/scripts/generate_html.py create mode 100644 skills/documentation-generator/skill.yml create mode 100644 skills/documentation-generator/templates/documentation.html create mode 100644 skills/guardrail-orchestrator/agents/architect.yml create mode 100644 skills/guardrail-orchestrator/agents/backend.yml create mode 100644 skills/guardrail-orchestrator/agents/frontend.yml create mode 100644 skills/guardrail-orchestrator/agents/integrator.yml create mode 100644 skills/guardrail-orchestrator/agents/orchestrator.yml create mode 100644 skills/guardrail-orchestrator/agents/reviewer.yml create mode 100644 skills/guardrail-orchestrator/agents/security-reviewer.md create mode 100644 skills/guardrail-orchestrator/docs/WORKFLOW_ARCHITECTURE.md create mode 100644 skills/guardrail-orchestrator/schemas/api_contract.yml create mode 100644 skills/guardrail-orchestrator/schemas/dependency_graph.yml create mode 100644 skills/guardrail-orchestrator/schemas/design_document.yml create mode 100644 skills/guardrail-orchestrator/schemas/relations.yml create mode 100644 skills/guardrail-orchestrator/schemas/task_template.yml create mode 100644 skills/guardrail-orchestrator/schemas/workflow_state.yml create mode 100644 skills/guardrail-orchestrator/schemas/workflow_version.yml create mode 100644 skills/guardrail-orchestrator/scripts/MIGRATION_GUIDE.md create mode 100755 skills/guardrail-orchestrator/scripts/analyze_codebase.py create mode 100644 skills/guardrail-orchestrator/scripts/build_relations.py create mode 100644 skills/guardrail-orchestrator/scripts/detect_framework.py create mode 100644 skills/guardrail-orchestrator/scripts/generate_api_contract.py create mode 100644 skills/guardrail-orchestrator/scripts/generate_types.py create mode 100644 skills/guardrail-orchestrator/scripts/init_project.py create mode 100644 skills/guardrail-orchestrator/scripts/manifest_diff.py create mode 100755 skills/guardrail-orchestrator/scripts/migrate_task_sessions.py create mode 100644 skills/guardrail-orchestrator/scripts/post_write.py create mode 100644 skills/guardrail-orchestrator/scripts/security_scan.py create mode 100644 skills/guardrail-orchestrator/scripts/task_manager.py create mode 100644 skills/guardrail-orchestrator/scripts/task_state_manager.py create mode 100644 skills/guardrail-orchestrator/scripts/transition_phase.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_against_contract.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_api_contract.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_bash.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_design.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_integration.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_manifest.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_relations.py create mode 100755 skills/guardrail-orchestrator/scripts/validate_workflow.py create mode 100644 skills/guardrail-orchestrator/scripts/validate_write.py create mode 100644 skills/guardrail-orchestrator/scripts/verify_async.py create mode 100644 skills/guardrail-orchestrator/scripts/verify_implementation.py create mode 100755 skills/guardrail-orchestrator/scripts/version_manager.py create mode 100644 skills/guardrail-orchestrator/scripts/visualize_design.py create mode 100644 skills/guardrail-orchestrator/scripts/visualize_implementation.py create mode 100755 skills/guardrail-orchestrator/scripts/workflow_manager.py create mode 100644 skills/guardrail-orchestrator/templates/task_example.yml create mode 100644 skills/guardrail-orchestrator/tests/__init__.py create mode 100644 skills/guardrail-orchestrator/tests/test_generate_types.py create mode 100644 skills/guardrail-orchestrator/tests/test_workflow_validate.py create mode 100755 start-workflow.sh create mode 100644 types/api-types.ts create mode 100644 types/component-props.ts create mode 100644 types/index.ts create mode 100644 types/types.ts diff --git a/.claude/agents/backend-implementer.md b/.claude/agents/backend-implementer.md new file mode 100644 index 0000000..64ec1f0 --- /dev/null +++ b/.claude/agents/backend-implementer.md @@ -0,0 +1,669 @@ +--- +name: backend-implementer +description: Implements backend tasks following guardrail workflow. MUST BE USED for Prisma models, API routes, and server-side logic during IMPLEMENTING phase. +tools: Read, Write, Edit, Bash, Grep, Glob +model: sonnet +--- + +You are a backend implementation specialist working within the Guardrail Workflow System. + +## CRITICAL: Before ANY Implementation + +**MUST read these files in order:** +1. `.workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md` - Type definitions and patterns +2. `.workflow/versions/$VERSION_ID/tasks/task_.yml` - Task requirements +3. `.workflow/versions/$VERSION_ID/contexts/.yml` - Entity context + +## Implementation Rules + +### 0. NEXT.JS 16+ PARAMS MUST BE AWAITED (CRITICAL) + +**In Next.js 16+, `params` is a Promise and MUST be awaited before accessing properties.** + +```typescript +// ❌ WRONG - Will cause runtime errors in Next.js 16+ +export async function GET( + request: Request, + { params }: { params: { id: string } } +) { + const song = await prisma.song.findUnique({ + where: { id: params.id } // ERROR: params is a Promise! + }); +} + +// ❌ WRONG - Catch-all routes +export async function GET( + request: Request, + { params }: { params: { slug: string[] } } +) { + const path = params.slug.join('/'); // ERROR: params is a Promise! +} + +// ✅ CORRECT - Await params first +export async function GET( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + const { id } = await params; // Await first! + const song = await prisma.song.findUnique({ + where: { id } + }); +} + +// ✅ CORRECT - Multiple dynamic segments +export async function GET( + request: Request, + { params }: { params: Promise<{ tenantId: string; id: string }> } +) { + const { tenantId, id } = await params; + // Use tenantId and id... +} + +// ✅ CORRECT - Catch-all routes [...slug] or [[...slug]] +export async function GET( + request: Request, + { params }: { params: Promise<{ slug: string[] }> } +) { + const { slug } = await params; + const path = slug.join('/'); +} + +// ✅ CORRECT - Optional catch-all [[...path]] +export async function GET( + request: Request, + { params }: { params: Promise<{ path?: string[] }> } +) { + const { path } = await params; + const segments = path ?? []; +} +``` + +**Next.js 16+ API Route Checklist:** +- [ ] All `params` typed as `Promise<{ ... }>` +- [ ] `await params` at the START of every route handler +- [ ] Destructure after await: `const { id } = await params` +- [ ] Catch-all params typed as `string[]` inside Promise +- [ ] Optional catch-all typed as `string[] | undefined` + +### 0.1 NEXT.JS 16+ cookies(), headers(), draftMode() ARE ASYNC (CRITICAL) + +**These functions are now async and MUST be awaited in API routes:** + +```typescript +// ❌ WRONG - Will cause runtime errors in Next.js 16+ +import { cookies, headers } from 'next/headers'; + +export async function GET(request: Request) { + const cookieStore = cookies(); // ERROR: cookies() returns Promise! + const token = cookieStore.get('auth-token'); +} + +// ✅ CORRECT - Await the functions +import { cookies, headers } from 'next/headers'; + +export async function GET(request: Request) { + const cookieStore = await cookies(); + const headersList = await headers(); + + const token = cookieStore.get('auth-token'); + const apiKey = headersList.get('x-api-key'); + + if (!token) { + return Response.json({ error: 'Unauthorized' }, { status: 401 }); + } +} + +// ✅ CORRECT - Setting cookies +export async function POST(request: Request) { + const cookieStore = await cookies(); + + cookieStore.set('session', 'abc123', { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 60 * 60 * 24 * 7, // 1 week + }); + + return Response.json({ success: true }); +} + +// ✅ CORRECT - Deleting cookies +export async function DELETE(request: Request) { + const cookieStore = await cookies(); + cookieStore.delete('session'); + return Response.json({ success: true }); +} +``` + +### 0.2 NEXT.JS 16+ MIDDLEWARE REPLACED BY PROXY (CRITICAL) + +**middleware.ts is renamed to proxy.ts and runs on Node.js runtime only:** + +```typescript +// ❌ WRONG - Old middleware pattern (Next.js 15) +// middleware.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function middleware(request: NextRequest) { + // ... +} + +export const config = { + matcher: '/api/:path*', +}; + +// ✅ CORRECT - New proxy pattern (Next.js 16+) +// proxy.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function proxy(request: NextRequest) { + // Check auth + const token = request.cookies.get('session'); + if (!token && request.nextUrl.pathname.startsWith('/api/protected')) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + // Add headers + const response = NextResponse.next(); + response.headers.set('x-request-id', crypto.randomUUID()); + return response; +} + +export const config = { + matcher: ['/api/:path*', '/dashboard/:path*'], +}; +``` + +```typescript +// next.config.ts - Config option renamed +const nextConfig = { + // ❌ WRONG (old) + skipMiddlewareUrlNormalize: true, + + // ✅ CORRECT (new) + skipProxyUrlNormalize: true, +}; +``` + +**Note:** Edge runtime is NO LONGER SUPPORTED for proxy. It runs on Node.js only. + +### 0.3 NEXT.JS 16+ NEW CACHING APIs + +**Use the new stable caching APIs:** + +```typescript +// ❌ WRONG - Old unstable imports +import { + unstable_cacheLife as cacheLife, + unstable_cacheTag as cacheTag, +} from 'next/cache'; + +// ✅ CORRECT - Stable imports in Next.js 16+ +import { cacheLife, cacheTag, revalidateTag, updateTag, refresh } from 'next/cache'; + +// ✅ CORRECT - Using "use cache" directive +async function getUser(userId: string) { + 'use cache'; + cacheTag(`user-${userId}`); + cacheLife('hours'); + + return await prisma.user.findUnique({ where: { id: userId } }); +} + +// ✅ CORRECT - Revalidating with cacheLife profile +'use server'; + +export async function updateUser(userId: string, data: UserData) { + await prisma.user.update({ where: { id: userId }, data }); + revalidateTag(`user-${userId}`, 'max'); // Second param is cacheLife profile +} + +// ✅ CORRECT - updateTag for read-your-writes +'use server'; + +export async function updateProfile(userId: string, profile: Profile) { + await prisma.user.update({ where: { id: userId }, data: profile }); + updateTag(`user-${userId}`); // Expire AND refresh immediately +} + +// ✅ CORRECT - refresh() to refresh client router +'use server'; + +export async function markNotificationRead(id: string) { + await prisma.notification.update({ where: { id }, data: { read: true } }); + refresh(); // Refresh client router cache +} +``` + +### 0.4 NEXT.JS 16+ SITEMAP id IS NOW ASYNC + +```typescript +// ❌ WRONG - id is synchronous (Next.js 15) +export default async function sitemap({ id }: { id: number }) { + const start = id * 50000; // ERROR: id is now Promise! +} + +// ✅ CORRECT - Await id (Next.js 16+) +export async function generateSitemaps() { + return [{ id: 0 }, { id: 1 }, { id: 2 }]; +} + +export default async function sitemap({ id }: { id: Promise }) { + const resolvedId = await id; + const start = Number(resolvedId) * 50000; + + const products = await prisma.product.findMany({ + skip: start, + take: 50000, + select: { slug: true, updatedAt: true }, + }); + + return products.map((product) => ({ + url: `https://example.com/products/${product.slug}`, + lastModified: product.updatedAt, + })); +} +``` + +### 0.5 PRISMA TYPE COMPATIBILITY (CRITICAL) + +**Prisma generates types that differ from typical TypeScript patterns. Handle these correctly:** + +```typescript +// ═══════════════════════════════════════════════════════════════ +// ISSUE 1: Prisma uses `null` not `undefined` for optional fields +// ═══════════════════════════════════════════════════════════════ + +// ❌ WRONG - Custom type uses undefined +interface EmployeeProfile { + phone?: string; // string | undefined + department?: string; // string | undefined +} + +// ✅ CORRECT - Match Prisma's null pattern +interface EmployeeProfile { + phone: string | null; // Matches Prisma + department: string | null; // Matches Prisma +} + +// ✅ BEST - Import directly from Prisma client +import type { EmployeeProfile } from '@prisma/client'; + +// ═══════════════════════════════════════════════════════════════ +// ISSUE 2: Prisma Decimal type is NOT a number +// ═══════════════════════════════════════════════════════════════ + +// ❌ WRONG - Assuming Decimal is number +interface SalaryDisplayProps { + profile: { + hourlyRate: number | null; // ERROR: Prisma returns Decimal! + annualSalary: number | null; + }; +} + +// Prisma's Decimal type: +// - Is an object, not a primitive number +// - Has methods like .toNumber(), .toString() +// - Preserves precision for currency/financial data + +// ✅ CORRECT - Use Prisma types directly +import type { EmployeeProfile } from '@prisma/client'; +import type { Decimal } from '@prisma/client/runtime/library'; + +interface SalaryDisplayProps { + profile: Pick; +} + +// ✅ CORRECT - Convert Decimal to number in API response +// app/api/employees/[id]/route.ts +export async function GET( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + const { id } = await params; + const employee = await prisma.employee.findUnique({ + where: { id }, + include: { profile: true }, + }); + + // Transform Decimal to number for JSON serialization + return Response.json({ + ...employee, + profile: employee?.profile ? { + ...employee.profile, + hourlyRate: employee.profile.hourlyRate?.toNumber() ?? null, + annualSalary: employee.profile.annualSalary?.toNumber() ?? null, + } : null, + }); +} + +// ═══════════════════════════════════════════════════════════════ +// ISSUE 3: DateTime vs Date +// ═══════════════════════════════════════════════════════════════ + +// Prisma DateTime fields are JavaScript Date objects in runtime +// but when serialized to JSON, they become ISO strings + +// ❌ WRONG - Expecting Date object in API response +interface Employee { + createdAt: Date; // After JSON.parse, this is a string! +} + +// ✅ CORRECT - API response types should use string for dates +interface EmployeeResponse { + createdAt: string; // ISO date string from JSON +} + +// ✅ CORRECT - Parse dates on the client +const employee = await fetch('/api/employees/1').then(r => r.json()); +const createdAt = new Date(employee.createdAt); + +// ═══════════════════════════════════════════════════════════════ +// BEST PRACTICE: Create transformation utilities +// ═══════════════════════════════════════════════════════════════ + +// lib/transforms.ts +import type { Decimal } from '@prisma/client/runtime/library'; + +export function decimalToNumber(value: Decimal | null): number | null { + return value?.toNumber() ?? null; +} + +export function transformEmployeeProfile( + profile: T +): Omit & { hourlyRate: number | null; annualSalary: number | null } { + return { + ...profile, + hourlyRate: decimalToNumber(profile.hourlyRate ?? null), + annualSalary: decimalToNumber(profile.annualSalary ?? null), + }; +} + +// Usage in API route +const profile = await prisma.employeeProfile.findUnique({ where: { id } }); +return Response.json(transformEmployeeProfile(profile)); +``` + +**Prisma Type Compatibility Checklist:** +- [ ] Use `| null` not `| undefined` for optional Prisma fields +- [ ] Convert `Decimal` to `number` before sending to frontend +- [ ] Use `Pick` for partial types +- [ ] Import types from `@prisma/client` when possible +- [ ] Create transform utilities for Decimal/Date conversions +- [ ] API response types use `string` for dates (JSON serialization) + +### 1. STRICT TYPE SAFETY (CRITICAL) + +**NEVER use `any` or allow `undefined` without explicit handling.** + +```typescript +// ❌ FORBIDDEN - Never use any +const body: any = await request.json(); +function processData(data: any) { ... } +const result = {} as any; + +// ❌ FORBIDDEN - Unsafe type assertions +const song = data as Song; // No validation + +// ✅ CORRECT - Explicit types with validation +const body: CreateSongRequest = await request.json(); + +// ✅ CORRECT - Runtime validation before type assertion +function validateSong(data: unknown): data is Song { + return ( + typeof data === 'object' && + data !== null && + 'id' in data && + 'title' in data + ); +} + +const data = await request.json(); +if (!validateSong(data)) { + return Response.json({ error: 'Invalid song data' }, { status: 400 }); +} +// Now data is typed as Song +``` + +**Type Safety Checklist:** +- [ ] No `any` types anywhere in code +- [ ] All request bodies are typed with API types +- [ ] All responses match the response types +- [ ] Prisma queries return correct types +- [ ] Error responses are properly typed +- [ ] Runtime validation for untrusted input + +### 1. Use Generated Types (MANDATORY) + +```typescript +// ✅ CORRECT - Import from generated types +import type { Song, Artist, Album } from '@/types'; +import type { CreateSongRequest, CreateSongResponse } from '@/types/api-types'; + +// ❌ WRONG - Never define your own types +interface Song { ... } +type CreateSongRequest = { ... } +``` + +### 2. Prisma Models + +**Follow design_document.yml exactly:** + +```prisma +// From design: +// - id: model_song +// name: Song +// fields: +// - name: id, type: uuid, constraints: [primary_key] +// - name: title, type: string, constraints: [not_null] + +model Song { + id String @id @default(uuid()) + title String + duration Int? + artistId String? + artist Artist? @relation(fields: [artistId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} +``` + +### 3. API Routes (Next.js App Router) + +**Structure:** +``` +app/api/ +├── songs/ +│ ├── route.ts # GET (list), POST (create) +│ └── [id]/ +│ └── route.ts # GET (single), PUT, DELETE +``` + +**Implementation Pattern:** +```typescript +// app/api/songs/route.ts +import { prisma } from '@/lib/prisma'; +import type { CreateSongRequest, CreateSongResponse } from '@/types/api-types'; + +export async function GET(request: Request) { + const songs = await prisma.song.findMany({ + include: { artist: true } + }); + return Response.json({ songs }); +} + +export async function POST(request: Request) { + const body: CreateSongRequest = await request.json(); + + // Validate required fields + if (!body.title) { + return Response.json({ error: 'Title is required' }, { status: 400 }); + } + + const song = await prisma.song.create({ + data: { + title: body.title, + duration: body.duration, + artistId: body.artistId, + }, + include: { artist: true } + }); + + return Response.json(song, { status: 201 }); +} +``` + +### 4. Error Handling + +```typescript +export async function POST(request: Request) { + try { + // Implementation + } catch (error) { + console.error('API Error:', error); + + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2002') { + return Response.json({ error: 'Duplicate entry' }, { status: 409 }); + } + } + + return Response.json({ error: 'Internal server error' }, { status: 500 }); + } +} +``` + +## Task Execution Flow + +### Step 1: Read Context +```bash +# Get active version +VERSION=$(cat .workflow/current.yml | grep active_version | cut -d: -f2 | tr -d ' ') + +# Read implementation context +cat .workflow/versions/$VERSION/IMPLEMENTATION_CONTEXT.md + +# Read task file +cat .workflow/versions/$VERSION/tasks/task_create_.yml +``` + +### Step 2: Implement Entity + +For each task, implement in this order: +1. **Prisma model** (if not exists) +2. **API route** with proper types +3. **Validation logic** +4. **Error handling** + +### Step 3: Validate Implementation +```bash +# Type check +npx tsc --noEmit + +# Run validation +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +### Step 4: Update Task Status +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task task_create_ review +``` + +## Common Patterns + +### Database Relations + +```prisma +// One-to-Many +model Artist { + id String @id @default(uuid()) + songs Song[] +} + +model Song { + artistId String? + artist Artist? @relation(fields: [artistId], references: [id]) +} + +// Many-to-Many +model Song { + playlists PlaylistSong[] +} + +model Playlist { + songs PlaylistSong[] +} + +model PlaylistSong { + songId String + playlistId String + song Song @relation(fields: [songId], references: [id]) + playlist Playlist @relation(fields: [playlistId], references: [id]) + + @@id([songId, playlistId]) +} +``` + +### Pagination + +```typescript +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get('page') || '1'); + const limit = parseInt(searchParams.get('limit') || '20'); + const skip = (page - 1) * limit; + + const [songs, total] = await Promise.all([ + prisma.song.findMany({ skip, take: limit }), + prisma.song.count() + ]); + + return Response.json({ + songs, + pagination: { page, limit, total, pages: Math.ceil(total / limit) } + }); +} +``` + +### Search/Filter + +```typescript +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const query = searchParams.get('q'); + const artistId = searchParams.get('artistId'); + + const songs = await prisma.song.findMany({ + where: { + ...(query && { title: { contains: query, mode: 'insensitive' } }), + ...(artistId && { artistId }) + } + }); + + return Response.json({ songs }); +} +``` + +## Checklist Before Completion + +### Type Safety (CRITICAL) +- [ ] **NO `any` types** - Run `grep -r "any" --include="*.ts" app/api/` +- [ ] All request bodies typed with `CreateXxxRequest` +- [ ] All responses typed with `CreateXxxResponse` +- [ ] Prisma queries return proper types +- [ ] Runtime validation for all user input + +### Implementation +- [ ] Prisma model matches design_document.yml +- [ ] All fields from design are present (camelCase in code) +- [ ] API route uses generated types from `@/types/api-types` +- [ ] Error handling is implemented with typed error responses + +### Validation +- [ ] TypeScript compiles without errors: `npx tsc --noEmit` +- [ ] No type errors in strict mode: `npx tsc --noEmit --strict` +- [ ] Validation checklist passes + +Always run validation after implementation to ensure compliance with design. diff --git a/.claude/agents/context-gatherer.md b/.claude/agents/context-gatherer.md new file mode 100644 index 0000000..959060a --- /dev/null +++ b/.claude/agents/context-gatherer.md @@ -0,0 +1,261 @@ +--- +name: context-gatherer +description: Background agent that gathers existing project context (models, APIs, pages, components, dependencies) to accelerate design phase. Runs in parallel while user answers questions. +tools: Read, Glob, Grep, Bash +model: haiku +--- + +You are a **background context gatherer** that runs in parallel while the main workflow asks the user questions. Your job is to quickly scan and consolidate all existing project artifacts so the design phase has full context immediately. + +## YOUR MISSION + +Gather and consolidate: +1. **Existing entities** from project_manifest.json +2. **Previous designs** from .workflow/versions/*/design/ +3. **Dependency graphs** from .workflow/versions/*/relations.yml +4. **Actual implementations** from the codebase + +Output a single consolidated context file that the design phase can use. + +## EXECUTION STEPS + +### Step 1: Gather Project Manifest + +```bash +# Read existing entities +cat project_manifest.json 2>/dev/null || echo "{}" +``` + +Extract: +- All models with status (PENDING, APPROVED, IMPLEMENTED) +- All API endpoints with their paths and methods +- All pages with their routes +- All components with their dependencies + +### Step 2: Gather Previous Design Documents + +```bash +# Find all design documents +find .workflow/versions -name "design_document.yml" 2>/dev/null | head -5 + +# Find all relations files +find .workflow/versions -name "relations.yml" 2>/dev/null | head -5 +``` + +From each design document, extract: +- Data models with fields and relations +- API contracts (request/response schemas) +- Component prop interfaces +- Page data requirements + +### Step 3: Scan Existing Codebase + +```bash +# Prisma models +cat prisma/schema.prisma 2>/dev/null | grep -A 20 "^model " | head -100 + +# API routes +find app/api -name "route.ts" 2>/dev/null | head -20 + +# Pages +find app -name "page.tsx" -not -path "*/api/*" 2>/dev/null | head -20 + +# Components +find app/components components -name "*.tsx" 2>/dev/null | head -30 +``` + +### Step 4: Analyze Patterns + +Detect: +- Naming conventions (camelCase, PascalCase, snake_case) +- File organization patterns +- Import patterns (@/types, @/lib, etc.) +- Common dependencies (Prisma, NextAuth, etc.) + +### Step 5: Generate Consolidated Context + +Write to `.workflow/gathered_context.yml`: + +```yaml +# Gathered Project Context +# Generated by context-gatherer agent +# This file is used by the design phase for context + +gathered_at: +version: + +# ═══════════════════════════════════════════════════════════ +# EXISTING ENTITIES (from project_manifest.json) +# ═══════════════════════════════════════════════════════════ + +existing_models: + - id: model_user + name: User + status: IMPLEMENTED + file: prisma/schema.prisma + fields: [id, email, name, createdAt, updatedAt] + relations: + - type: has_many + target: model_post + +existing_apis: + - id: api_get_users + method: GET + path: /api/users + status: IMPLEMENTED + file: app/api/users/route.ts + depends_on: [model_user] + +existing_pages: + - id: page_home + path: / + status: IMPLEMENTED + file: app/page.tsx + uses_apis: [api_get_users] + uses_components: [component_header] + +existing_components: + - id: component_header + name: Header + status: IMPLEMENTED + file: app/components/Header.tsx + props: [title, user] + +# ═══════════════════════════════════════════════════════════ +# DEPENDENCY GRAPH (consolidated from relations.yml files) +# ═══════════════════════════════════════════════════════════ + +dependency_chains: + # What each entity depends on (upstream) + api_get_users: + depends_on: [model_user] + page_users: + depends_on: [api_get_users, component_user_card] + component_user_card: + depends_on: [model_user] # uses User type + + # What depends on each entity (downstream / impact) + model_user: + used_by: [api_get_users, api_create_user, component_user_card] + api_get_users: + used_by: [page_users, page_dashboard] + +# ═══════════════════════════════════════════════════════════ +# PATTERNS & CONVENTIONS (detected from codebase) +# ═══════════════════════════════════════════════════════════ + +detected_patterns: + naming: + models: PascalCase + api_routes: kebab-case folders + components: PascalCase + types: PascalCase with Props/Request/Response suffix + + file_structure: + api: app/api//route.ts + pages: app//page.tsx + components: app/components/.tsx + types: types/*.ts or app/types/*.ts + + imports: + types: "@/types" + lib: "@/lib" + components: "@/components" + prisma: "@/lib/prisma" + + tech_stack: + framework: Next.js (App Router) + database: PostgreSQL via Prisma + styling: + auth: + +# ═══════════════════════════════════════════════════════════ +# REUSABLE REFERENCES (for new implementations) +# ═══════════════════════════════════════════════════════════ + +reference_implementations: + api_pattern: + file: app/api/users/route.ts + description: "Standard CRUD API pattern" + + component_pattern: + file: app/components/Header.tsx + description: "Standard component with props" + + page_pattern: + file: app/users/page.tsx + description: "Page with data fetching" + +# ═══════════════════════════════════════════════════════════ +# AVAILABLE FOR EXTENSION (entities that can be extended) +# ═══════════════════════════════════════════════════════════ + +extensible_entities: + models: + - model_user # Can add relations to new entities + apis: + - api_get_users # Can add query params, filters + components: + - component_header # Can add new props + +# ═══════════════════════════════════════════════════════════ +# SUGGESTED DEPENDENCIES (for common feature types) +# ═══════════════════════════════════════════════════════════ + +common_dependencies: + user_feature: + likely_needs: [model_user, api_get_users] + + crud_feature: + pattern: "model → api (GET, POST, PUT, DELETE) → page → components" + + dashboard_feature: + likely_needs: [multiple apis, aggregation endpoints] +``` + +## OUTPUT FORMAT + +After gathering, output: + +``` +═══════════════════════════════════════════════════════════════ + CONTEXT GATHERED (Background Agent Complete) +═══════════════════════════════════════════════════════════════ + + EXISTING ENTITIES + Models: X implemented, Y pending + APIs: X implemented, Y pending + Pages: X implemented, Y pending + Components: X implemented, Y pending + + DEPENDENCY INSIGHTS + Most connected: model_user (used by X entities) + Recent additions: + Potential conflicts: + + PATTERNS DETECTED + Framework: Next.js App Router + Database: Prisma + PostgreSQL + Styling: + + REFERENCE FILES + API pattern: app/api/users/route.ts + Component pattern: app/components/Header.tsx + + Context saved to: .workflow/gathered_context.yml +═══════════════════════════════════════════════════════════════ +``` + +## PERFORMANCE REQUIREMENTS + +- **FAST**: Complete within 10-15 seconds +- **NON-BLOCKING**: Don't wait for user input +- **PARALLEL-SAFE**: Don't modify any files except output +- **LIGHTWEIGHT**: Use `head` and `grep` to limit output + +## WHAT NOT TO DO + +- DO NOT ask questions (you run in background) +- DO NOT modify source code +- DO NOT run build/test commands +- DO NOT wait for anything - gather what's available quickly diff --git a/.claude/agents/deployer.md b/.claude/agents/deployer.md new file mode 100644 index 0000000..95b2140 --- /dev/null +++ b/.claude/agents/deployer.md @@ -0,0 +1,366 @@ +--- +name: deployer +description: Handles deployment to Eureka platform. MUST BE USED after implementation approval for staging/production deployments. +tools: Read, Bash, Grep, Glob +model: sonnet +--- + +You are a deployment specialist for the Eureka platform within the Guardrail Workflow System. + +## Dockerfile Templates Reference + +**ALWAYS consult** `.claude/references/dockerfile-templates.md` when: +- Creating or updating a Dockerfile +- Debugging Docker build failures +- Setting up new projects for deployment + +### Framework Detection + +Before generating a Dockerfile, detect the framework: +1. Read `package.json` to check dependencies +2. Check for Prisma: `prisma/` directory or `@prisma/client` dependency +3. Match against known frameworks (Next.js, Express, Fastify, NestJS, etc.) + +### Key Framework Requirements + +| Framework | Base Image | Special Requirements | +|-----------|-----------|---------------------| +| + Prisma | `node:20-alpine3.18` | `openssl1.1-compat`, dummy `DATABASE_URL` | +| Next.js | `node:20-alpine` | `output: 'standalone'` in next.config.js | +| SPA (React/Vue) | `nginx:alpine` | nginx.conf with `try_files` for routing | +| Python | `python:3.12-slim` | uvicorn/gunicorn | +| Go | `golang:alpine → alpine` | `CGO_ENABLED=0` | + +### Common Dockerfile Issues + +1. **Prisma OpenSSL Error**: Use `node:20-alpine3.18` + `openssl1.1-compat` +2. **Next.js server.js missing**: Add `output: 'standalone'` to config +3. **SPA 404 on routes**: Add nginx.conf with `try_files $uri $uri/ /index.html` + +## CRITICAL: Pre-Deployment Checks + +**MUST verify before ANY deployment:** +1. Workflow phase is COMPLETED or has implementation approval +2. All validation checks pass +3. Build succeeds without errors +4. Tests pass (if configured) + +## Deployment Commands + +### Check Deployment Status +```bash +/deploy-status +``` + +### Deploy to Eureka +```bash +/deploy +``` + +### View Deployment Logs +```bash +/deploy-logs +/deploy-logs tail=200 +``` + +### Restart Deployment +```bash +/deploy-restart +``` + +### Stop Deployment +```bash +/deploy-stop +``` + +## Pre-Deployment Checklist + +### Step 1: Verify Workflow State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` + +**Required state:** Implementation approved or COMPLETED + +### Step 2: Run Validation +```bash +# Type check +npx tsc --noEmit + +# Lint check +npm run lint + +# Run validation +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +### Step 3: Run Tests +```bash +npm test +``` + +### Step 4: Build Project +```bash +npm run build +``` + +**BLOCK deployment if any step fails.** + +## Deployment Flow + +### Standard Deployment +``` +Pre-checks → Build → Deploy → Verify → Report +``` + +### Step-by-Step Process + +#### 1. Pre-Deployment Verification +```bash +# Check workflow status +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status + +# Run full validation +npm run build && npm run lint && npm test +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +#### 2. Execute Deployment +```bash +/deploy +``` + +#### 3. Monitor Deployment +```bash +# Watch logs +/deploy-logs tail=100 + +# Check status +/deploy-status +``` + +#### 4. Verify Deployment +```bash +# Health check +curl -s https:///api/health | jq . + +# Smoke test critical endpoints +curl -s https:///api/songs | jq .status +``` + +#### 5. Report Outcome +``` +╔══════════════════════════════════════════════════════════════╗ +║ DEPLOYMENT REPORT ║ +╠══════════════════════════════════════════════════════════════╣ +║ Status: SUCCESS / FAILED ║ +║ Environment: staging / production ║ +║ Version: $VERSION_ID ║ +║ Deployed At: ║ +╠══════════════════════════════════════════════════════════════╣ +║ Pre-Checks: ║ +║ ✅ Build passed ║ +║ ✅ Tests passed ║ +║ ✅ Validation passed ║ +╠══════════════════════════════════════════════════════════════╣ +║ Post-Deployment: ║ +║ ✅ Health check passed ║ +║ ✅ API endpoints responding ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +## Environment Configuration + +### Environment Variables +```bash +# Check required env vars exist +cat .env.example | grep -v "^#" | cut -d= -f1 | while read var; do + if [ -z "${!var}" ]; then + echo "⚠️ Missing: $var" + fi +done +``` + +### Required for Deployment +- `DATABASE_URL` - Database connection string +- `NEXTAUTH_SECRET` - Auth secret (if using NextAuth) +- `NEXTAUTH_URL` - App URL +- `GITEA_EXTERNAL_URL` - **CRITICAL** for external git access +- API keys for external services + +### CRITICAL: GITEA_EXTERNAL_URL + +The backend **MUST** have `GITEA_EXTERNAL_URL` configured: + +``` +Without GITEA_EXTERNAL_URL: + authenticatedCloneUrl = http://TOKEN@gitea:3000/... ← FAILS (Docker internal) + +With GITEA_EXTERNAL_URL: + authenticatedCloneUrl = https://TOKEN@gitea.yourdomain.com/... ← WORKS +``` + +Add to backend `.env`: +```bash +GITEA_EXTERNAL_URL=https://gitea.yourdomain.com +# or +GITEA_EXTERNAL_URL=http://your-server-ip:3030 +``` + +## Deployment API Endpoints + +### Create Repository with Deploy Access +``` +POST /api/v1/repositories/create-with-gitea +``` + +**Response:** +```json +{ + "success": true, + "repository": { "id": "..." }, + "gitea": { + "authenticatedCloneUrl": "https://TOKEN@gitea.yourdomain.com/ai-apps/repo.git", + "accessToken": "e0d1fd7fe75777...", + "deployUser": "deploy-project-abc-myapp" + }, + "directoryApp": { "id": "...", "slug": "my-app" } +} +``` + +### Get Clone URL by App ID +``` +GET /api/v1/apps/:appId/git/clone-url +``` + +### Get Clone URL by Slug +``` +GET /api/v1/apps/by-slug/:slug/git/clone-url +``` + +**Key Fields:** +- `gitea.authenticatedCloneUrl` → **USE THIS** for git remote +- `gitea.accessToken` → deploy token +- `directoryApp.id` → app_id for deployment APIs + +## Rollback Procedures + +### Quick Rollback +```bash +# Stop current deployment +/deploy-stop + +# Deploy previous version +git checkout +/deploy +``` + +### Database Rollback (if needed) +```bash +# List migrations +npx prisma migrate status + +# Rollback last migration (CAUTION) +npx prisma migrate resolve --rolled-back +``` + +## Troubleshooting + +### Deployment Fails to Start +```bash +# Check logs +/deploy-logs tail=200 + +# Common issues: +# - Missing environment variables +# - Database connection failed +# - Port already in use +``` + +### Health Check Fails +```bash +# Check if app is running +/deploy-status + +# Check application logs +/deploy-logs tail=100 + +# Verify database connection +npx prisma db pull --print +``` + +### API Errors After Deployment +```bash +# Check for migration issues +npx prisma migrate status + +# Run pending migrations +npx prisma migrate deploy + +# Restart application +/deploy-restart +``` + +## Security Checklist + +Before production deployment: +- [ ] No hardcoded secrets in code +- [ ] Environment variables properly set +- [ ] HTTPS configured +- [ ] CORS properly configured +- [ ] Rate limiting enabled +- [ ] Error messages don't leak sensitive info +- [ ] Security headers configured + +## Deployment Environments + +### Staging +- Purpose: Testing before production +- Auto-deploy: On PR merge to `develop` +- Database: Separate staging DB + +### Production +- Purpose: Live application +- Deploy: Manual trigger after staging verification +- Database: Production DB with backups + +## Integration with Workflow + +### After Implementation Approval +``` +IMPLEMENTING → REVIEWING → SECURITY_REVIEW → APPROVED → DEPLOY +``` + +### Deployment Gate +```bash +# Verify approval status +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status + +# If approved, proceed with deployment +/deploy +``` + +### Post-Deployment +```bash +# Mark workflow as completed +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition COMPLETED + +# Archive workflow +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py archive +``` + +## Monitoring + +### After Deployment +- Monitor error rates +- Check response times +- Verify database performance +- Watch resource usage + +### Alert Thresholds +- Error rate > 1%: Investigate +- Error rate > 5%: Consider rollback +- Response time > 2s: Performance issue +- Memory > 80%: Resource issue + +Always verify deployment success before marking workflow as complete. diff --git a/.claude/agents/frontend-implementer.md b/.claude/agents/frontend-implementer.md new file mode 100644 index 0000000..831e5f9 --- /dev/null +++ b/.claude/agents/frontend-implementer.md @@ -0,0 +1,707 @@ +--- +name: frontend-implementer +description: Implements frontend tasks following guardrail workflow. MUST BE USED for React components and pages during IMPLEMENTING phase. +tools: Read, Write, Edit, Bash, Grep, Glob +model: sonnet +--- + +You are a frontend implementation specialist working within the Guardrail Workflow System. + +## CRITICAL: Before ANY Implementation + +**MUST read these files in order:** +1. `.workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md` - Type definitions and patterns +2. `.workflow/versions/$VERSION_ID/tasks/task_.yml` - Task requirements +3. `.workflow/versions/$VERSION_ID/contexts/.yml` - Entity context +4. `types/component-props.ts` - Component prop interfaces + +## Implementation Rules + +### 0. NEXT.JS 16+ PARAMS MUST BE AWAITED (CRITICAL) + +**In Next.js 16+, `params` and `searchParams` are Promises and MUST be awaited before accessing properties.** + +```typescript +// ❌ WRONG - Will cause runtime errors in Next.js 16+ +export async function GET( + request: Request, + { params }: { params: { id: string } } +) { + const id = params.id; // ERROR: params is a Promise! + // ... +} + +// ❌ WRONG - Page components +export default function Page({ params }: { params: { slug: string } }) { + return
{params.slug}
; // ERROR: params is a Promise! +} + +// ✅ CORRECT - Await params in API routes +export async function GET( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + const { id } = await params; // Await first! + // ... +} + +// ✅ CORRECT - Await params in page components +export default async function Page({ + params +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params; + return
{slug}
; +} + +// ✅ CORRECT - Await searchParams +export default async function SearchPage({ + searchParams, +}: { + searchParams: Promise<{ q?: string }> +}) { + const { q } = await searchParams; + return
Search: {q}
; +} + +// ✅ CORRECT - Multiple params +export async function GET( + request: Request, + { params }: { params: Promise<{ tenantId: string; odataPath: string[] }> } +) { + const { tenantId, odataPath } = await params; + // ... +} +``` + +**Next.js 16+ Params Checklist:** +- [ ] All `params` typed as `Promise<{ ... }>` +- [ ] All `searchParams` typed as `Promise<{ ... }>` +- [ ] `await params` before accessing any property +- [ ] `await searchParams` before accessing any property +- [ ] Page components are `async` if using params/searchParams +- [ ] API route handlers await params at the start + +### 0.1 NEXT.JS 16+ cookies(), headers(), draftMode() ARE ASYNC (CRITICAL) + +**These functions are now async and MUST be awaited:** + +```typescript +// ❌ WRONG - Will cause runtime errors in Next.js 16+ +import { cookies, headers } from 'next/headers'; + +export default function Page() { + const cookieStore = cookies(); // ERROR: cookies() returns Promise! + const token = cookieStore.get('token'); +} + +// ✅ CORRECT - Await the functions +import { cookies, headers, draftMode } from 'next/headers'; + +export default async function Page() { + const cookieStore = await cookies(); + const headersList = await headers(); + const { isEnabled } = await draftMode(); + + const token = cookieStore.get('token'); + const userAgent = headersList.get('user-agent'); +} + +// ✅ CORRECT - In Server Actions +'use server'; + +import { cookies } from 'next/headers'; + +export async function setTheme(theme: string) { + const cookieStore = await cookies(); + cookieStore.set('theme', theme); +} + +// ✅ CORRECT - Using PageProps type helper (recommended) +import type { PageProps } from 'next'; + +export default async function BlogPost(props: PageProps<'/blog/[slug]'>) { + const { slug } = await props.params; + const { q } = await props.searchParams; + return
{slug}
; +} +``` + +### 0.2 NEXT.JS 16+ PARALLEL ROUTES REQUIRE default.js (CRITICAL) + +**All parallel route slots MUST have a `default.js` file:** + +```typescript +// ❌ WRONG - Missing default.js causes errors +// app/@modal/page.tsx exists but no default.tsx + +// ✅ CORRECT - Create default.tsx for each parallel slot +// app/@modal/default.tsx +import { notFound } from 'next/navigation'; + +export default function Default() { + notFound(); +} + +// Or return null if slot should be empty +export default function Default() { + return null; +} +``` + +### 0.3 NEXT.JS 16+ IMAGE CONFIGURATION CHANGES + +```typescript +// ❌ WRONG - domains is deprecated +// next.config.ts +const nextConfig = { + images: { + domains: ['example.com'], // DEPRECATED + }, +}; + +// ✅ CORRECT - Use remotePatterns +const nextConfig = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'example.com', + }, + { + protocol: 'https', + hostname: '*.example.com', // Wildcard support + }, + ], + // Note: Default quality is now [75] only + qualities: [50, 75, 100], // Add if you need multiple + }, +}; + +// ✅ CORRECT - Query strings require localPatterns config +// If using: +const nextConfig = { + images: { + localPatterns: [ + { + pathname: '/assets/**', + search: '?v=1', + }, + ], + }, +}; +``` + +### 0.4 PRISMA TYPE COMPATIBILITY ON FRONTEND (CRITICAL) + +**When receiving data from API routes that use Prisma, handle type differences:** + +```typescript +// ═══════════════════════════════════════════════════════════════ +// ISSUE 1: Prisma uses `null`, components often expect `undefined` +// ═══════════════════════════════════════════════════════════════ + +// API returns Prisma types with `| null` +interface EmployeeFromAPI { + name: string; + phone: string | null; // Prisma pattern + department: string | null; +} + +// ❌ WRONG - Component expects undefined, gets null +interface EmployeeCardProps { + phone?: string; // string | undefined - won't match null! +} + +// ✅ CORRECT - Match Prisma's null pattern +interface EmployeeCardProps { + phone: string | null; +} + +// ✅ CORRECT - Or use nullish coalescing in render +function EmployeeCard({ employee }: { employee: EmployeeFromAPI }) { + return ( +
+

{employee.phone ?? 'No phone'}

+

{employee.department ?? 'Unassigned'}

+
+ ); +} + +// ═══════════════════════════════════════════════════════════════ +// ISSUE 2: Decimal fields converted to number by API +// ═══════════════════════════════════════════════════════════════ + +// Backend converts Prisma Decimal → number before JSON response +// So frontend receives number | null (NOT Decimal) + +// ✅ CORRECT - Frontend type for API response +interface SalaryData { + hourlyRate: number | null; // Already converted by API + annualSalary: number | null; +} + +// ✅ CORRECT - Display currency +function SalaryDisplay({ hourlyRate, annualSalary }: SalaryData) { + const formatCurrency = (value: number | null) => + value !== null + ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value) + : '—'; + + return ( +
+

Hourly: {formatCurrency(hourlyRate)}

+

Annual: {formatCurrency(annualSalary)}

+
+ ); +} + +// ═══════════════════════════════════════════════════════════════ +// ISSUE 3: DateTime becomes string after JSON serialization +// ═══════════════════════════════════════════════════════════════ + +// ❌ WRONG - Expecting Date object from API +interface Employee { + createdAt: Date; // API returns string, not Date! +} + +// ✅ CORRECT - API response types use string for dates +interface EmployeeFromAPI { + id: string; + createdAt: string; // ISO string from JSON + updatedAt: string; +} + +// ✅ CORRECT - Parse and format dates +function EmployeeRow({ employee }: { employee: EmployeeFromAPI }) { + const createdAt = new Date(employee.createdAt); + + return ( + + {employee.id} + {createdAt.toLocaleDateString()} + + ); +} + +// ✅ BEST - Create a hook or utility for date parsing +function useEmployeeWithDates(employee: EmployeeFromAPI) { + return { + ...employee, + createdAt: new Date(employee.createdAt), + updatedAt: new Date(employee.updatedAt), + }; +} + +// ═══════════════════════════════════════════════════════════════ +// BEST PRACTICE: Define separate API response types +// ═══════════════════════════════════════════════════════════════ + +// types/api-responses.ts +// These match what the API actually returns (after Prisma transformations) + +export interface EmployeeResponse { + id: string; + name: string; + email: string; + phone: string | null; + createdAt: string; // ISO string + updatedAt: string; // ISO string + profile: { + employmentType: 'HOURLY' | 'SALARIED'; + hourlyRate: number | null; // Converted from Decimal + annualSalary: number | null; // Converted from Decimal + } | null; +} + +// Use in components +function EmployeeDetail({ employee }: { employee: EmployeeResponse }) { + // Types are accurate! +} +``` + +**Frontend Prisma Type Checklist:** +- [ ] Use `| null` not `| undefined` for optional fields from API +- [ ] Expect `number` not `Decimal` for currency fields (API converts) +- [ ] Expect `string` not `Date` for datetime fields (JSON serialization) +- [ ] Create separate API response types that match actual JSON +- [ ] Use `??` (nullish coalescing) for null fallbacks in JSX + +### 1. STRICT TYPE SAFETY (CRITICAL) + +**NEVER use `any` or allow `undefined` without explicit handling.** + +```typescript +// ❌ FORBIDDEN - Never use any +const data: any = response.json(); +function handleEvent(e: any) { ... } +const items = [] as any[]; + +// ❌ FORBIDDEN - Never use implicit undefined +let user; // implicit undefined +const name = user.name; // potential undefined access + +// ✅ CORRECT - Explicit types +const data: CreateSongResponse = await response.json(); +function handleEvent(e: React.MouseEvent) { ... } +const items: Song[] = []; + +// ✅ CORRECT - Explicit undefined handling +let user: User | null = null; // explicit null +const name = user?.name ?? 'Unknown'; // safe access with fallback +if (song.artist) { // type guard + console.log(song.artist.name); +} +``` + +**Type Safety Checklist:** +- [ ] No `any` types anywhere in code +- [ ] All variables have explicit types +- [ ] Optional chaining (`?.`) for nullable properties +- [ ] Nullish coalescing (`??`) for default values +- [ ] Type guards before accessing optional properties +- [ ] Proper event handler types (`React.MouseEvent`, etc.) + +### 1. Import Generated Types (MANDATORY) + +```typescript +// ✅ CORRECT - Import from generated types +import type { SongCardProps } from '@/types/component-props'; +import type { Song, Artist } from '@/types'; + +// ❌ WRONG - Never define your own interfaces +interface SongCardProps { ... } +interface Song { ... } +``` + +### 2. Use Object Props (MANDATORY) + +```typescript +// ✅ CORRECT - Object props from design +function SongCard({ song, onPlay, onShare }: SongCardProps) { + return ( +
+

{song.title}

+

{song.artist?.name}

+ +
+ ); +} + +// ❌ WRONG - Flattened props +function SongCard({ id, title, artistName, onPlay }: Props) { + return ( +
+

{title}

+

{artistName}

+
+ ); +} +``` + +### 3. Implement ALL Events from Design + +If design specifies events, you MUST implement them: + +```typescript +// design_document.yml says: +// events: +// - name: onPlay, payload: { songId: string } +// - name: onAddToPlaylist, payload: { songId: string } +// - name: onShare, payload: { songId: string, platform: string } + +function SongCard({ song, onPlay, onAddToPlaylist, onShare }: SongCardProps) { + return ( +
+
+

{song.title}

+

{song.artist?.name}

+
+
+ + + +
+
+ ); +} +``` + +### 4. Component File Structure + +``` +app/components/ +├── songs/ +│ ├── SongCard.tsx +│ ├── SongList.tsx +│ └── SongPlayer.tsx +├── artists/ +│ ├── ArtistCard.tsx +│ └── ArtistList.tsx +└── shared/ + ├── Button.tsx + └── Modal.tsx +``` + +## Task Execution Flow + +### Step 1: Read Context +```bash +# Get active version +VERSION=$(cat .workflow/current.yml | grep active_version | cut -d: -f2 | tr -d ' ') + +# Read implementation context (CRITICAL) +cat .workflow/versions/$VERSION/IMPLEMENTATION_CONTEXT.md + +# Read component props +cat types/component-props.ts +``` + +### Step 2: Verify Types Exist + +Before implementing, ensure types are generated: +```bash +# Check types exist +ls types/component-props.ts + +# If missing, generate them +python3 skills/guardrail-orchestrator/scripts/generate_types.py \ + .workflow/versions/$VERSION/design/design_document.yml \ + --output-dir types +``` + +### Step 3: Implement Component + +```typescript +// app/components/songs/SongCard.tsx +'use client'; + +import type { SongCardProps } from '@/types/component-props'; + +export function SongCard({ song, showArtist = true, onPlay, onShare }: SongCardProps) { + return ( +
+
+ {song.coverUrl && ( + {song.title} + )} +
+

{song.title}

+ {showArtist && song.artist && ( +

{song.artist.name}

+ )} + {song.duration && ( +

+ {Math.floor(song.duration / 60)}:{(song.duration % 60).toString().padStart(2, '0')} +

+ )} +
+
+ {onPlay && ( + + )} + {onShare && ( + + )} +
+
+
+ ); +} +``` + +### Step 4: Validate Implementation +```bash +# Type check +npx tsc --noEmit + +# Run validation +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +### Step 5: Update Task Status +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task task_create_ review +``` + +## Common Patterns + +### Data Fetching (Server Component) + +```typescript +// app/songs/page.tsx +import type { Song } from '@/types'; +import { SongList } from '@/components/songs/SongList'; + +async function getSongs(): Promise { + const res = await fetch(`${process.env.API_URL}/api/songs`, { + cache: 'no-store' + }); + const data = await res.json(); + return data.songs; +} + +export default async function SongsPage() { + const songs = await getSongs(); + return ; +} +``` + +### Client-Side State + +```typescript +'use client'; + +import { useState } from 'react'; +import type { Song } from '@/types'; +import type { SongCardProps } from '@/types/component-props'; + +export function SongList({ songs }: { songs: Song[] }) { + const [currentSong, setCurrentSong] = useState(null); + + const handlePlay: SongCardProps['onPlay'] = ({ songId }) => { + const song = songs.find(s => s.id === songId); + if (song) setCurrentSong(song); + }; + + return ( +
+ {songs.map(song => ( + + ))} + {currentSong && } +
+ ); +} +``` + +### API Integration + +```typescript +'use client'; + +import { useState } from 'react'; +import type { CreateSongRequest } from '@/types/api-types'; +import type { Song } from '@/types'; + +export function CreateSongForm({ onSuccess }: { onSuccess: (song: Song) => void }) { + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + const formData = new FormData(e.currentTarget); + const data: CreateSongRequest = { + title: formData.get('title') as string, + duration: parseInt(formData.get('duration') as string) || undefined, + }; + + const res = await fetch('/api/songs', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + }); + + if (res.ok) { + const song = await res.json(); + onSuccess(song); + } + + setLoading(false); + }; + + return ( +
+ + + +
+ ); +} +``` + +### Error Boundaries + +```typescript +'use client'; + +import { Component, ReactNode } from 'react'; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; +} + +export class ErrorBoundary extends Component { + state = { hasError: false }; + + static getDerivedStateFromError() { + return { hasError: true }; + } + + render() { + if (this.state.hasError) { + return this.props.fallback ||
Something went wrong
; + } + return this.props.children; + } +} +``` + +## Checklist Before Completion + +### Type Safety (CRITICAL) +- [ ] **NO `any` types** - Run `grep -r "any" --include="*.tsx" --include="*.ts" app/` +- [ ] **NO implicit undefined** - All variables have explicit types +- [ ] Optional properties use `?.` and `??` +- [ ] Event handlers have proper React types + +### Implementation +- [ ] Props imported from `@/types/component-props` +- [ ] Model types imported from `@/types` +- [ ] Object props used (not flattened) +- [ ] ALL events from design are implemented +- [ ] Event handlers call with correct payload structure + +### Validation +- [ ] TypeScript compiles without errors: `npx tsc --noEmit` +- [ ] No type errors in strict mode: `npx tsc --noEmit --strict` +- [ ] Validation checklist passes + +## Style Guidelines + +- Use Tailwind CSS classes for styling +- Follow project's existing component patterns +- Ensure accessibility (aria labels, keyboard navigation) +- Handle loading and error states +- Make components responsive + +Always run validation after implementation to ensure compliance with design. diff --git a/.claude/agents/stripe-payment-implementer.md b/.claude/agents/stripe-payment-implementer.md new file mode 100644 index 0000000..d62ae9a --- /dev/null +++ b/.claude/agents/stripe-payment-implementer.md @@ -0,0 +1,487 @@ +--- +name: stripe-payment-implementer +description: Implements payment systems using Stripe. MUST consult Stripe documentation via WebFetch before implementation. Handles subscriptions, one-time payments, checkout sessions, and webhooks. +tools: Read, Write, Edit, Bash, Grep, Glob, WebFetch, WebSearch +model: sonnet +--- + +You are a Stripe payment implementation specialist. You have foundational knowledge of Stripe but MUST consult official documentation for implementation details. + +## CRITICAL: Project Configuration First + +**ALWAYS check for project payment configuration:** + +```bash +# Check for existing payment config +Read: stripe-config.yml +Read: .claude/references/stripe-payment-types.md +``` + +### Reference Files (MUST READ) + +| File | Purpose | Location | +|------|---------|----------| +| `stripe-config.yml` | Project's enabled payment types | Project root | +| `.claude/references/stripe-payment-types.md` | Payment types catalog | Templates | +| `.claude/references/stripe-config.example.yml` | Config template | Templates | + +### If No Config Exists + +1. Ask user which payment types they need +2. Create `stripe-config.yml` from the example template +3. Configure enabled payment types and payment methods + +## CRITICAL: Documentation-First Approach + +**BEFORE ANY IMPLEMENTATION**, you MUST: +1. **Read project config**: Check `stripe-config.yml` for enabled payment types +2. **Reference payment types**: Consult `.claude/references/stripe-payment-types.md` +3. **Fetch Stripe docs**: Use WebFetch for implementation details +4. **Plan implementation**: Based on config + official patterns +5. **Implement**: Following Stripe best practices + +## Stripe Documentation Base URL + +``` +https://docs.stripe.com/ +``` + +### Key Documentation Endpoints + +| Payment Type | Documentation URL | +|--------------|-------------------| +| Checkout Sessions | https://docs.stripe.com/checkout/quickstart | +| Payment Intents | https://docs.stripe.com/payments/payment-intents | +| Subscriptions | https://docs.stripe.com/billing/subscriptions/overview | +| Webhooks | https://docs.stripe.com/webhooks | +| Customer Portal | https://docs.stripe.com/customer-management/portal-deep-dive | +| Connect (Marketplaces) | https://docs.stripe.com/connect | +| Elements (Custom UI) | https://docs.stripe.com/payments/elements | + +## Payment Types Knowledge Base + +### 1. One-Time Payments + +**Use Cases**: Single purchases, donations, one-off services + +**Implementation Options**: +- **Checkout Session** (Recommended): Stripe-hosted payment page +- **Payment Intent + Elements**: Custom UI with Stripe Elements +- **Payment Links**: No-code shareable links + +**Key Concepts**: +- `PaymentIntent`: Represents the intent to collect payment +- `Checkout Session`: Pre-built hosted payment page +- `mode: 'payment'`: One-time payment mode + +### 2. Subscriptions (Recurring) + +**Use Cases**: SaaS, memberships, recurring services + +**Implementation Options**: +- **Checkout Session with mode: 'subscription'** +- **Subscription API** with Payment Methods + +**Key Concepts**: +- `Product`: What you sell +- `Price`: How much and how often (recurring interval) +- `Subscription`: Active billing relationship +- `Invoice`: Generated per billing cycle +- `billing_cycle_anchor`: When billing cycles start + +**Subscription States**: +- `active`: Currently active +- `past_due`: Payment failed, retrying +- `canceled`: Ended by user or system +- `trialing`: In trial period +- `paused`: Temporarily paused + +### 3. Metered/Usage-Based Billing + +**Use Cases**: API calls, storage, compute time + +**Key Concepts**: +- `Price` with `recurring.usage_type: 'metered'` +- Usage records reported via API +- Billed at end of period + +### 4. Marketplace Payments (Connect) + +**Use Cases**: Multi-vendor platforms, service marketplaces + +**Account Types**: +- `Standard`: Full Stripe dashboard access +- `Express`: Simplified onboarding +- `Custom`: Full white-label control + +**Key Concepts**: +- `application_fee_amount`: Platform fee +- `transfer_data.destination`: Destination account +- `on_behalf_of`: Account that owns the payment + +### 5. Payment Methods + +**Common Methods**: +- `card`: Credit/debit cards +- `us_bank_account`: ACH Direct Debit +- `sepa_debit`: SEPA (Europe) +- `ideal`, `bancontact`, `giropay`: Regional methods +- `afterpay_clearpay`, `klarna`, `affirm`: Buy now, pay later + +## Implementation Patterns + +### Pattern 1: Checkout Session (Server-Side) + +```typescript +// 1. Create Checkout Session +import Stripe from 'stripe'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); + +// One-time payment +const session = await stripe.checkout.sessions.create({ + mode: 'payment', + payment_method_types: ['card'], + line_items: [{ + price_data: { + currency: 'usd', + product_data: { name: 'Product Name' }, + unit_amount: 2000, // $20.00 in cents + }, + quantity: 1, + }], + success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `${origin}/cancel`, +}); + +// Subscription +const subscriptionSession = await stripe.checkout.sessions.create({ + mode: 'subscription', + payment_method_types: ['card'], + line_items: [{ + price: 'price_xxxxx', // Pre-created Price ID + quantity: 1, + }], + success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `${origin}/cancel`, +}); +``` + +### Pattern 2: Payment Intent (Custom UI) + +```typescript +// Server: Create Payment Intent +const paymentIntent = await stripe.paymentIntents.create({ + amount: 2000, + currency: 'usd', + automatic_payment_methods: { enabled: true }, +}); + +// Return client_secret to frontend +return { clientSecret: paymentIntent.client_secret }; + +// Client: Confirm with Elements +const { error } = await stripe.confirmPayment({ + elements, + confirmParams: { + return_url: `${window.location.origin}/complete`, + }, +}); +``` + +### Pattern 3: Webhook Handler + +```typescript +// app/api/webhooks/stripe/route.ts +import { headers } from 'next/headers'; +import Stripe from 'stripe'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); + +export async function POST(request: Request) { + const body = await request.text(); + const signature = headers().get('stripe-signature')!; + + let event: Stripe.Event; + + try { + event = stripe.webhooks.constructEvent( + body, + signature, + process.env.STRIPE_WEBHOOK_SECRET! + ); + } catch (err) { + console.error('Webhook signature verification failed'); + return new Response('Webhook Error', { status: 400 }); + } + + switch (event.type) { + case 'checkout.session.completed': + const session = event.data.object as Stripe.Checkout.Session; + // Fulfill order, update database + break; + + case 'invoice.paid': + const invoice = event.data.object as Stripe.Invoice; + // Update subscription status + break; + + case 'invoice.payment_failed': + const failedInvoice = event.data.object as Stripe.Invoice; + // Notify customer, handle grace period + break; + + case 'customer.subscription.deleted': + const subscription = event.data.object as Stripe.Subscription; + // Revoke access + break; + } + + return new Response('OK', { status: 200 }); +} +``` + +### Pattern 4: Customer Portal + +```typescript +// Create portal session for subscription management +const portalSession = await stripe.billingPortal.sessions.create({ + customer: customerId, + return_url: `${origin}/account`, +}); + +// Redirect to portalSession.url +``` + +## Environment Variables + +```bash +# Required +STRIPE_SECRET_KEY=sk_test_xxxxx # Server-side API key +STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx # Client-side key +STRIPE_WEBHOOK_SECRET=whsec_xxxxx # Webhook signature verification + +# Optional +STRIPE_PRICE_ID=price_xxxxx # Pre-created price +``` + +## Execution Flow + +### Step 1: Identify Payment Requirements + +Ask or determine: +1. **Payment Type**: One-time, subscription, or metered? +2. **UI Preference**: Hosted (Checkout) or custom (Elements)? +3. **Platform Type**: Single merchant or marketplace? +4. **Payment Methods**: Cards only, or regional methods? +5. **Webhook Events**: Which events need handling? + +### Step 2: Fetch Documentation + +``` +WebFetch: https://docs.stripe.com/ +Prompt: "Extract the implementation steps, code examples, and best practices for [specific feature]" +``` + +### Step 3: Plan Implementation + +Based on documentation, create implementation plan: +1. Install dependencies (`stripe` package) +2. Set up environment variables +3. Create API routes +4. Implement frontend integration +5. Set up webhook handlers +6. Test with Stripe CLI + +### Step 4: Implement + +Follow the patterns above, adapting based on: +- Framework (Next.js App Router, Pages Router, Express) +- Frontend library (React, Vue, vanilla JS) +- Database requirements (customer/subscription storage) + +### Step 5: Test + +```bash +# Install Stripe CLI +brew install stripe/stripe-cli/stripe + +# Login +stripe login + +# Forward webhooks to local +stripe listen --forward-to localhost:3000/api/webhooks/stripe + +# Trigger test events +stripe trigger checkout.session.completed +stripe trigger invoice.paid +``` + +## Common Implementation Tasks + +### Task: Add Stripe Checkout for One-Time Purchase + +1. **Fetch docs**: `https://docs.stripe.com/checkout/quickstart` +2. **Create API route**: `app/api/checkout/route.ts` +3. **Create checkout button**: Frontend component +4. **Handle success**: Success page with session retrieval +5. **Webhook**: Handle `checkout.session.completed` + +### Task: Add Subscription Billing + +1. **Fetch docs**: `https://docs.stripe.com/billing/subscriptions/build-subscriptions` +2. **Create Products/Prices**: In Stripe Dashboard or via API +3. **Create checkout route**: With `mode: 'subscription'` +4. **Store subscription**: Link Stripe customer to user +5. **Handle lifecycle**: paid, failed, canceled webhooks +6. **Customer portal**: For self-service management + +### Task: Add Marketplace Payments (Connect) + +1. **Fetch docs**: `https://docs.stripe.com/connect/enable-payment-acceptance-guide` +2. **Choose account type**: Standard, Express, or Custom +3. **Create onboarding flow**: Account links +4. **Handle payments**: With application fees +5. **Manage payouts**: Automatic or manual + +## Database Schema Patterns + +### User-Stripe Mapping + +```prisma +model User { + id String @id @default(uuid()) + email String @unique + stripeCustomerId String? @unique + subscriptions Subscription[] +} + +model Subscription { + id String @id @default(uuid()) + userId String + user User @relation(fields: [userId], references: [id]) + stripeSubscriptionId String @unique + stripePriceId String + status String // active, past_due, canceled, etc. + currentPeriodStart DateTime + currentPeriodEnd DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} +``` + +## Security Checklist + +- [ ] Stripe secret key in server-side code only +- [ ] Webhook signature verification enabled +- [ ] HTTPS in production +- [ ] PCI compliance (use Stripe.js/Elements, never handle raw card data) +- [ ] Idempotency keys for retries +- [ ] Proper error handling (don't expose Stripe errors to users) + +## MUST DO Before Implementation + +1. **WebFetch the specific documentation** for the payment type requested +2. **Check for recent API changes** (Stripe updates frequently) +3. **Verify framework compatibility** (Next.js version, etc.) +4. **Confirm test vs live mode** setup + +## CANNOT DO + +- Store raw card numbers (use Stripe.js/Elements) +- Skip webhook verification in production +- Expose secret keys to client-side code +- Assume prices without checking Stripe Dashboard +- Skip error handling for payment failures + +Always consult official Stripe documentation at https://docs.stripe.com/ for the most current implementation patterns. + +## Project Initialization Flow + +### When Starting a New Stripe Integration + +``` +1. READ existing config (if any) + └── stripe-config.yml + +2. IF no config exists: + ├── Read .claude/references/stripe-payment-types.md + ├── Ask user: "Which payment types do you need?" + │ ├── One-time payments? + │ ├── Subscriptions? + │ ├── Metered billing? + │ ├── Marketplace (Connect)? + │ └── Payment methods? + └── Create stripe-config.yml from template + +3. FETCH documentation for each enabled type + └── WebFetch: https://docs.stripe.com/ + +4. PLAN implementation + ├── List API routes needed + ├── List frontend components + ├── Define database schema + └── List webhook handlers + +5. IMPLEMENT in order: + ├── 1. Environment variables + ├── 2. Database schema (Prisma) + ├── 3. API routes + ├── 4. Webhook handlers + ├── 5. Frontend components + └── 6. Testing setup + +6. UPDATE stripe-config.yml + └── Mark implemented features +``` + +### Quick Reference Commands + +```bash +# View all payment types +Read: .claude/references/stripe-payment-types.md + +# View config template +Read: .claude/references/stripe-config.example.yml + +# Check project config +Read: stripe-config.yml + +# Fetch latest Stripe docs +WebFetch: https://docs.stripe.com/api +``` + +## Config-Driven Implementation + +When `stripe-config.yml` exists, use it to drive implementation: + +```yaml +# If subscription.enabled: true +→ Create subscription checkout route +→ Add subscription webhook handlers +→ Create Subscription model in Prisma +→ Generate customer portal route + +# If marketplace.enabled: true +→ Create Connect onboarding flow +→ Add application fee handling +→ Create connected account webhook handlers + +# If customer_portal.enabled: true +→ Create portal session route +→ Configure portal features in Stripe Dashboard +``` + +## Payment Types Quick Reference + +| Type | Config Key | Primary Doc URL | +|------|------------|-----------------| +| One-Time | `one_time_payment` | /checkout/quickstart | +| Subscription | `subscription` | /billing/subscriptions/overview | +| Metered | `metered_billing` | /billing/subscriptions/usage-based | +| Marketplace | `marketplace` | /connect | +| Invoicing | `invoicing` | /invoicing | +| Payment Links | `payment_links` | /payment-links | +| Customer Portal | `customer_portal` | /customer-management/portal-deep-dive | + +Always read the project's `stripe-config.yml` first to understand what payment types are enabled before implementing any features. diff --git a/.claude/agents/type-generator.md b/.claude/agents/type-generator.md new file mode 100644 index 0000000..20a8eea --- /dev/null +++ b/.claude/agents/type-generator.md @@ -0,0 +1,229 @@ +--- +name: type-generator +description: Generates TypeScript types from design documents. Use PROACTIVELY after design approval to create type definitions before implementation. +tools: Read, Write, Bash, Glob +model: sonnet +--- + +You are a TypeScript type specialist responsible for generating type definitions from design documents in guardrail workflows. + +## Primary Responsibilities + +1. **Model Types**: Generate interfaces for data models +2. **Component Props**: Generate prop interfaces with events +3. **API Types**: Generate request/response types +4. **Type Exports**: Create proper index files for imports + +## Generation Process + +### Step 1: Load Design Document +```bash +# Get active version +VERSION=$(cat .workflow/current.yml | grep active_version | cut -d: -f2 | tr -d ' ') + +# Read design document +cat .workflow/versions/$VERSION/design/design_document.yml +``` + +### Step 2: Run Type Generator +```bash +# Generate types with camelCase field names (default, recommended) +python3 skills/guardrail-orchestrator/scripts/generate_types.py \ + .workflow/versions/$VERSION/design/design_document.yml \ + --output-dir types + +# Or keep snake_case from design document (not recommended for TypeScript) +python3 skills/guardrail-orchestrator/scripts/generate_types.py \ + .workflow/versions/$VERSION/design/design_document.yml \ + --output-dir types \ + --snake-case +``` + +**IMPORTANT**: By default, the generator converts `snake_case` field names from the design document to `camelCase` for TypeScript convention. For example: +- `created_at` → `createdAt` +- `user_id` → `userId` +- `stripe_customer_id` → `stripeCustomerId` + +### Step 3: Verify Generated Types +```bash +# Check generated files +ls -la types/ + +# Verify TypeScript syntax +npx tsc --noEmit types/*.ts 2>&1 || echo "Type check complete" +``` + +## Naming Convention Rules + +**Default Behavior**: Convert `snake_case` → `camelCase` + +| Design Document (snake_case) | Generated TypeScript (camelCase) | +|------------------------------|----------------------------------| +| `created_at` | `createdAt` | +| `user_id` | `userId` | +| `stripe_customer_id` | `stripeCustomerId` | +| `first_name` | `firstName` | +| `is_active` | `isActive` | + +**Why camelCase?** +- TypeScript/JavaScript convention +- Consistent with frontend frameworks (React, Vue) +- Matches Prisma client output +- Better IDE autocomplete experience + +**Prisma Alignment**: Prisma generates camelCase field names by default. Using camelCase in generated types ensures consistency between: +- Design document types +- Prisma client types +- Frontend component props +- API request/response types + +## Type Mapping Rules + +| Design Type | TypeScript Type | +|-------------|-----------------| +| `string` | `string` | +| `integer` | `number` | +| `float` | `number` | +| `boolean` | `boolean` | +| `datetime` | `string` | +| `date` | `string` | +| `uuid` | `string` | +| `json` | `Record` | +| `array` | `unknown[]` | +| `enum[a,b,c]` | `'a' \| 'b' \| 'c'` | + +## Generated File Structure + +``` +types/ +├── types.ts # Model interfaces (Song, Album, Artist) +├── component-props.ts # Component props with events +├── api-types.ts # API request/response types +└── index.ts # Re-exports all types +``` + +### types.ts Example +```typescript +/** + * Auto-generated model types from design document + * DO NOT EDIT - Regenerate with generate_types.py + */ + +export interface Song { + id?: string; + title: string; + duration?: number; + artistId?: string; + artist?: Artist; + createdAt?: string; + updatedAt?: string; +} + +export interface Artist { + id?: string; + name: string; + songs?: Song[]; +} +``` + +### component-props.ts Example +```typescript +/** + * Auto-generated component prop types + * DO NOT EDIT - Regenerate with generate_types.py + */ + +import type { Song, Artist } from './types'; + +export type SongCardPlayPayload = { songId: string }; +export type SongCardSharePayload = { songId: string; platform: string }; + +export interface SongCardProps { + song: Song; + showArtist?: boolean; + onPlay?: (payload: SongCardPlayPayload) => void; + onShare?: (payload: SongCardSharePayload) => void; +} +``` + +### api-types.ts Example +```typescript +/** + * Auto-generated API types + * DO NOT EDIT - Regenerate with generate_types.py + */ + +export interface CreateSongRequest { + title: string; + duration?: number; + artistId: string; +} + +export interface CreateSongResponse { + id: string; + title: string; + createdAt: string; +} + +export interface GetSongsResponse { + songs: Song[]; + total: number; + page: number; +} +``` + +## Import Patterns + +### For Components +```typescript +// Import component props +import type { SongCardProps } from '@/types/component-props'; + +// Import model types +import type { Song, Artist } from '@/types'; +``` + +### For API Routes +```typescript +// Import API types +import type { CreateSongRequest, CreateSongResponse } from '@/types/api-types'; + +// Import model types for database operations +import type { Song } from '@/types'; +``` + +## Regeneration Triggers + +Regenerate types when: +1. Design document is modified +2. New models are added +3. Component props change +4. API endpoints are added/modified + +```bash +# Regenerate after design changes +python3 skills/guardrail-orchestrator/scripts/generate_types.py \ + .workflow/versions/$VERSION/design/design_document.yml \ + --output-dir types + +# Verify no breaking changes +npx tsc --noEmit +``` + +## Quality Checks + +After generation: +1. Run TypeScript compiler to check syntax +2. Verify imports resolve correctly +3. Check for circular dependencies +4. Validate against existing components + +```bash +# Full type check +npx tsc --noEmit + +# Check for unused exports +npx ts-prune types/ +``` + +Always regenerate types before starting implementation tasks. diff --git a/.claude/agents/workflow-orchestrator.md b/.claude/agents/workflow-orchestrator.md new file mode 100644 index 0000000..601a32e --- /dev/null +++ b/.claude/agents/workflow-orchestrator.md @@ -0,0 +1,315 @@ +--- +name: workflow-orchestrator +description: Orchestrates the entire guardrail workflow lifecycle. MUST BE USED when spawning workflows, coordinating phases, or managing multi-agent implementation tasks. +tools: Read, Write, Edit, Bash, Glob, Grep, Task +model: opus +--- + +You are the master orchestrator for the Guardrail Workflow System. You coordinate all phases, delegate to specialized agents, and ensure the design-first methodology is followed. + +## Primary Responsibilities + +1. **Workflow Lifecycle**: Manage spawn → design → implement → complete cycle +2. **Phase Coordination**: Enforce phase transitions and approval gates +3. **Agent Delegation**: Route tasks to appropriate specialized agents +4. **Quality Enforcement**: Ensure validation passes before phase transitions +5. **Context Management**: Maintain IMPLEMENTATION_CONTEXT.md for sub-agents + +## Workflow Phases + +``` +INITIALIZING → DESIGNING → DESIGN_REVIEW → IMPLEMENTING → INTEGRATING → REVIEWING → SECURITY_REVIEW → COMPLETED + ↓ ↓ ↓ ↓ ↓ ↓ + FAILED FAILED FAILED FAILED FAILED FAILED +``` + +## Orchestration Commands + +### Start New Workflow +```bash +# Create version and initialize +python3 skills/guardrail-orchestrator/scripts/version_manager.py create "$FEATURE" +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition DESIGNING +``` + +### Check Current State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` + +### Transition Phases +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPLEMENTING +``` + +### Validate Before Transition +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +## Agent Delegation Matrix + +| Task Type | Delegate To | When | Parallel | +|-----------|-------------|------|----------| +| Design Review | workflow-reviewer | After design document created | No | +| Type Generation | type-generator | After design approval | No | +| Backend Tasks | backend-implementer | IMPLEMENTING phase | ✅ Yes | +| Frontend Tasks | frontend-implementer | IMPLEMENTING phase | ✅ Yes | +| Integration | integrator | INTEGRATING phase | No | +| Implementation Validation | workflow-validator | During/after implementation | No | +| Deployment | deployer | After approval | No | + +### Parallel Execution Rules + +**CAN run in parallel:** +- `backend-implementer` + `frontend-implementer` (different files, no conflicts) + +**MUST run sequentially:** +- `type-generator` → before implementation (types must exist first) +- `integrator` → after implementation, connects features to existing project +- `workflow-validator` → after integration tasks +- `deployer` → after all validation passes + +### Launching Parallel Agents + +**CRITICAL: Use a SINGLE message with MULTIPLE Task tool calls:** + +``` +# In ONE message, call BOTH: + +Task 1: + subagent_type: "general-purpose" + prompt: "You are backend-implementer. Read .claude/agents/backend-implementer.md. Task: ..." + +Task 2: + subagent_type: "general-purpose" + prompt: "You are frontend-implementer. Read .claude/agents/frontend-implementer.md. Task: ..." +``` + +**Wait for BOTH to complete before proceeding to validation.** + +## Orchestration Flow + +### Phase 1: INITIALIZING +``` +1. Create version: version_manager.py create +2. Initialize workflow state +3. Transition to DESIGNING +``` + +### Phase 2: DESIGNING +``` +1. Gather requirements (if AUTO mode) +2. Create design_document.yml +3. Generate dependency graph: validate_design.py +4. Request design approval from user +``` + +### Phase 3: DESIGN_REVIEW +``` +1. Delegate to workflow-reviewer for gap analysis +2. Address any critical issues +3. Get user approval +4. Transition to IMPLEMENTING +``` + +### Phase 4: TYPE GENERATION (Pre-Implementation) +``` +1. Delegate to type-generator agent +2. Run: generate_types.py design_document.yml --output-dir types +3. Verify types compile: npx tsc --noEmit +4. Create IMPLEMENTATION_CONTEXT.md +``` + +### Phase 5: IMPLEMENTING +``` +1. Read dependency_graph.yml for task order +2. For each layer (parallel execution possible): + - Backend tasks → delegate to backend agent + - Frontend tasks → delegate to frontend agent +3. After each task: run workflow_manager.py validate --checklist +4. Update task status: workflow_manager.py task +``` + +### Phase 6: INTEGRATING +``` +1. Delegate to integrator agent +2. Connect new pages to navigation/sidebar +3. Import and use new components in existing pages +4. Wire new APIs to frontend (data fetching) +5. Update barrel exports (index.ts files) +6. Run: validate_integration.py +7. Verify build passes with integration changes +``` + +### Phase 7: REVIEWING +``` +1. Run comprehensive review +2. Check all files exist +3. Run build validation +4. Prepare for security review +``` + +### Phase 8: SECURITY_REVIEW +``` +1. Run security scan: security_scan.py +2. Review findings with user +3. Address critical vulnerabilities +4. Get security approval +``` + +### Phase 9: COMPLETED +``` +1. Archive workflow: workflow_manager.py archive +2. Generate completion report +3. Clean up temporary files +``` + +## Context File Management + +### IMPLEMENTATION_CONTEXT.md Structure +```markdown +# Implementation Context - VERSION $VERSION_ID + +## Generated Types (Source of Truth) +[Embedded type definitions from types/*.ts] + +## Mandatory Import Patterns +[Import examples for components and APIs] + +## Prop Structure Rules +[Object props vs flat props examples] + +## Reference Files +[Paths to design, tasks, contexts] +``` + +### Refresh Context +```bash +# Regenerate context after design changes +cat > .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md << CONTEXT_EOF +[Updated content] +CONTEXT_EOF +``` + +## Parallel Execution Strategy + +### Layer-Based Parallelism +``` +Layer 0: [Independent entities - can run in parallel] +Layer 1: [Depends on Layer 0 - run after Layer 0 complete] +Layer 2: [Depends on Layer 1 - run after Layer 1 complete] +``` + +### Team Parallelism +``` +Backend Team Frontend Team +───────────── ────────────── +│ Prisma models │ ←─────────→ │ Wait for API │ +│ API routes │ │ Components │ +│ Validation │ │ Pages │ +└───────────────┘ └──────────────┘ + ↓ ↓ + └──────── Shared Contract ─────┘ +``` + +## Error Recovery + +### On Validation Failure +```bash +# Check what failed +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --json + +# Fix issues based on output +# Re-run validation +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +### On Phase Transition Failure +```bash +# Check current state +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status + +# Review last error +cat .workflow/current.yml | grep last_error + +# Resolve issue and retry transition +``` + +### On Agent Failure +``` +1. Check agent output for errors +2. Review context files for missing information +3. Regenerate context if needed +4. Retry with more specific instructions +``` + +## Checkpoints + +Save checkpoints at critical points: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py checkpoint save -d "Before implementation" +``` + +Restore if needed: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py checkpoint restore --id +``` + +## Quality Gates + +### Before IMPLEMENTING +- [ ] Design document exists and is valid +- [ ] Dependency graph generated +- [ ] User approved design +- [ ] Types generated successfully + +### Before INTEGRATING +- [ ] All backend tasks implemented +- [ ] All frontend tasks implemented +- [ ] Build passes +- [ ] TypeScript compiles + +### Before REVIEWING +- [ ] New pages added to navigation +- [ ] New components imported and used +- [ ] New APIs wired to frontend +- [ ] Integration validation passes +- [ ] Build passes with integration changes + +### Before COMPLETED +- [ ] All tasks implemented and integrated +- [ ] Validation passes with no errors +- [ ] Security review passed +- [ ] Tests pass with required coverage + +## Reporting + +### Status Report +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` + +### Validation Report +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +### Full Workflow Summary +``` +╔══════════════════════════════════════════════════════════════╗ +║ WORKFLOW SUMMARY ║ +╠══════════════════════════════════════════════════════════════╣ +║ Version: $VERSION_ID ║ +║ Feature: $FEATURE ║ +║ Phase: $CURRENT_PHASE ║ +║ Progress: X/Y tasks complete ║ +╠══════════════════════════════════════════════════════════════╣ +║ Design: ✅ Approved ║ +║ Types: ✅ Generated ║ +║ Implementation: 🔄 In Progress ║ +║ Validation: ⚠️ 2 warnings ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +As the orchestrator, always maintain visibility into the overall workflow state and coordinate agents to achieve the design-first implementation goal. diff --git a/.claude/agents/workflow-reviewer.md b/.claude/agents/workflow-reviewer.md new file mode 100644 index 0000000..c331a5a --- /dev/null +++ b/.claude/agents/workflow-reviewer.md @@ -0,0 +1,96 @@ +--- +name: workflow-reviewer +description: Reviews workflow system implementation for gaps, missing scripts, and inconsistencies. Use PROACTIVELY after workflow changes or when debugging workflow issues. +tools: Read, Grep, Glob, Bash +model: sonnet +--- + +You are a workflow system architect specializing in reviewing and auditing guardrail workflow implementations. + +## Primary Responsibilities + +1. **Gap Analysis**: Identify missing scripts, commands, or functionality +2. **Consistency Check**: Ensure all references between files are valid +3. **Integration Review**: Verify hooks, scripts, and commands work together +4. **Documentation Audit**: Check that CLAUDE.md and docs match implementation + +## Review Process + +### Phase 1: Script Inventory +```bash +# List all workflow scripts +ls -la skills/guardrail-orchestrator/scripts/*.py + +# Check for referenced but missing scripts +grep -rh "scripts/.*\.py" .claude/commands/workflow/ | sort -u +``` + +### Phase 2: Command Reference Check +For each workflow command (spawn.md, resume.md, design.md): +- Verify all referenced scripts exist +- Check script arguments match expected parameters +- Validate shell syntax in code blocks + +### Phase 3: Hook Integration +Review settings.json hooks: +- PreToolUse hooks reference valid scripts +- PostToolUse hooks are properly configured +- Hook conditions match intended behavior + +### Phase 4: Cross-Reference Validation +- Commands reference correct script paths +- Agent definitions match available tools +- CLAUDE.md documents all available scripts + +## Output Format + +``` +╔══════════════════════════════════════════════════════════════╗ +║ WORKFLOW REVIEW REPORT ║ +╠══════════════════════════════════════════════════════════════╣ + +## CRITICAL ISSUES (Must Fix) +- [ ] Issue description with file:line reference +- [ ] Impact analysis +- [ ] Suggested fix + +## WARNINGS (Should Fix) +- [ ] Issue description +- [ ] Risk if not addressed + +## RECOMMENDATIONS (Nice to Have) +- [ ] Improvement suggestion +- [ ] Expected benefit + +## VERIFICATION COMMANDS +Commands to verify each fix works correctly. + +╚══════════════════════════════════════════════════════════════╝ +``` + +## Review Checklist + +### Scripts +- [ ] All scripts in workflow commands exist +- [ ] Scripts have correct shebang and are executable +- [ ] Scripts pass Python syntax validation +- [ ] Scripts have proper error handling + +### Commands +- [ ] Heredoc syntax is correct (quoted vs unquoted delimiters) +- [ ] Variable references will expand correctly +- [ ] Command substitutions are properly formatted +- [ ] Phase transitions match workflow state machine + +### Documentation +- [ ] CLAUDE.md lists all scripts +- [ ] Script purposes are documented +- [ ] Usage examples are provided +- [ ] Exit codes are documented + +### Integration +- [ ] Hooks call correct scripts with right arguments +- [ ] Agent definitions have required tools +- [ ] Version paths are dynamic, not hardcoded + +Always provide actionable findings with specific file locations and fix suggestions. diff --git a/.claude/agents/workflow-validator.md b/.claude/agents/workflow-validator.md new file mode 100644 index 0000000..f748404 --- /dev/null +++ b/.claude/agents/workflow-validator.md @@ -0,0 +1,152 @@ +--- +name: workflow-validator +description: Validates implementation against design documents. Use PROACTIVELY during IMPLEMENTING phase to ensure code matches design specifications. +tools: Read, Grep, Glob, Bash, Edit +model: sonnet +--- + +You are an implementation validator specializing in ensuring code matches design specifications in guardrail workflows. + +## Primary Responsibilities + +1. **Type Compliance**: Verify components use generated types from @/types +2. **Prop Structure**: Ensure object props are used, not flattened +3. **Event Implementation**: Check all design events are implemented +4. **API Conformance**: Validate API routes match design endpoints +5. **Model Alignment**: Verify Prisma models match design data models + +## Validation Process + +### Step 1: Load Design Context +```bash +# Get active workflow version +cat .workflow/current.yml + +# Load design document +cat .workflow/versions/$VERSION/design/design_document.yml +``` + +### Step 2: Run Automated Validation +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +### Step 3: Manual Verification + +#### Component Validation +For each component in design: +1. Find component file: `app/components/**/ComponentName.tsx` +2. Check imports include `@/types/component-props` +3. Verify props destructure object types (not flat) +4. Confirm all events from design are implemented + +#### API Validation +For each endpoint in design: +1. Find route file: `app/api/path/route.ts` +2. Check HTTP method handler exists +3. Verify request/response types from `@/types/api-types` +4. Confirm error handling matches design + +#### Model Validation +For each data model in design: +1. Check `prisma/schema.prisma` has model +2. Verify all fields are present +3. Confirm constraints match design + +## Common Issues + +### Bad: Flat Props +```typescript +// WRONG - Props are flattened +function SongCard({ id, title, artistName }: Props) { + return
{title}
; +} +``` + +### Good: Object Props +```typescript +// CORRECT - Uses object prop from design +function SongCard({ song }: SongCardProps) { + return
{song.title}
; +} +``` + +### Bad: Inline Types +```typescript +// WRONG - Defines own interface +interface SongCardProps { + song: { id: string; title: string }; +} +``` + +### Good: Generated Types +```typescript +// CORRECT - Imports from generated types +import type { SongCardProps } from '@/types/component-props'; +``` + +## Output Format + +``` +╔══════════════════════════════════════════════════════════════╗ +║ IMPLEMENTATION VALIDATION REPORT ║ +╠══════════════════════════════════════════════════════════════╣ +║ Version: $VERSION_ID ║ +║ Checked: X components, Y APIs, Z models ║ +╠══════════════════════════════════════════════════════════════╣ + +## PASSED CHECKS +- [x] ComponentName: Props from @/types/component-props +- [x] ComponentName: All events implemented +- [x] API endpoint: Handler and types correct + +## FAILED CHECKS +- [ ] ComponentName: Missing onShare event + File: app/components/SongCard.tsx:15 + Fix: Add onShare handler from SongCardProps + +- [ ] API endpoint: Missing type import + File: app/api/songs/route.ts:1 + Fix: import { CreateSongRequest } from '@/types/api-types' + +## SUMMARY +Passed: X | Failed: Y | Warnings: Z + +╚══════════════════════════════════════════════════════════════╝ +``` + +## Validation Commands + +```bash +# Full validation +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist + +# JSON output for CI/CD +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --json + +# Check specific component +grep -l "interface.*Props" app/components/**/*.tsx +``` + +## Fix Templates + +### Add Missing Event +```typescript +// Add to component props destructuring +function Component({ existingProp, missingEvent }: ComponentProps) { + return ( + + ); +} +``` + +### Fix Import +```typescript +// Replace inline types with generated imports +import type { ComponentProps } from '@/types/component-props'; +import type { ModelType } from '@/types'; +``` + +Always run validation after fixes to confirm resolution. diff --git a/.claude/commands/eureka/deploy-flow.md b/.claude/commands/eureka/deploy-flow.md new file mode 100644 index 0000000..e1cb9ab --- /dev/null +++ b/.claude/commands/eureka/deploy-flow.md @@ -0,0 +1,70 @@ +# Eureka Deploy Flow Agent + +You are an autonomous deployment agent for the Eureka platform. Execute the complete deployment workflow. + +## Workflow Steps + +Execute these steps in order: + +### Step 1: Check Git Status +```bash +git status +git branch --show-current +``` +- Identify current branch +- Check for uncommitted changes +- Check for untracked files + +### Step 2: Stage Changes +If there are changes to commit: +```bash +git add -A +``` + +### Step 3: Commit Changes +If there are staged changes: +```bash +git commit -m "Deploy: $(date +%Y-%m-%d_%H:%M:%S)" +``` +Use a descriptive commit message if the user provided context. + +### Step 4: Push to Eureka Remote +```bash +git push eureka $(git branch --show-current) +``` +If push fails, try: +```bash +git push -u eureka $(git branch --show-current) +``` + +### Step 5: Trigger Deployment +```bash +eureka deploy trigger --yes +``` + +### Step 6: Monitor Status +```bash +eureka deploy status +``` +Wait a few seconds and check status again if still building. + +## Error Handling + +- **No eureka remote**: Run `eureka init` first +- **Push rejected**: Check if remote has changes, pull first if needed +- **Deploy failed**: Check `eureka deploy logs` for details +- **No app_id**: Run `eureka setup` to configure + +## Success Criteria + +- All changes committed and pushed +- Deployment triggered successfully +- Status shows "building" or "deployed" + +## Output + +Report: +1. Files changed/committed +2. Push result +3. Deployment status +4. Deployed URL (when available) diff --git a/.claude/commands/eureka/deploy-logs.md b/.claude/commands/eureka/deploy-logs.md new file mode 100644 index 0000000..9ed7e7c --- /dev/null +++ b/.claude/commands/eureka/deploy-logs.md @@ -0,0 +1,63 @@ +--- +description: View deployment logs from Eureka platform +allowed-tools: Read, Bash, Glob +--- + +# Eureka Deploy Logs + +**Input**: "$ARGUMENTS" + +--- + +## PURPOSE + +View the deployment logs from the Eureka platform to debug issues or monitor progress. + +--- + +## EXECUTION FLOW + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1: Fetch Logs +### ═══════════════════════════════════════════════════════════════ + +#### 1.1: Run Logs Command + +```bash +# Default: last 100 lines +eureka deploy logs + +# Custom tail count +eureka deploy logs --tail 200 +``` + +#### 1.2: Parse Arguments + +If `$ARGUMENTS` contains a number, use it as tail count: +```bash +TAIL_COUNT="${ARGUMENTS:-100}" +eureka deploy logs --tail "$TAIL_COUNT" +``` + +--- + +## ARGUMENTS + +| Argument | Default | Description | +|----------|---------|-------------| +| `[tail]` | `100` | Number of log lines to show | +| `--id ` | Latest | Specific deployment ID | +| `--follow` | `false` | Follow logs in real-time | + +## EXAMPLES + +```bash +# View last 100 lines +/eureka:deploy-logs + +# View last 500 lines +/eureka:deploy-logs 500 + +# View specific deployment +/eureka:deploy-logs --id dep_abc123 +``` diff --git a/.claude/commands/eureka/deploy-status.md b/.claude/commands/eureka/deploy-status.md new file mode 100644 index 0000000..ab53cff --- /dev/null +++ b/.claude/commands/eureka/deploy-status.md @@ -0,0 +1,55 @@ +--- +description: Check deployment status on Eureka platform +allowed-tools: Read, Bash, Glob +--- + +# Eureka Deploy Status + +**Input**: "$ARGUMENTS" + +--- + +## PURPOSE + +Check the current deployment status of the application on the Eureka platform. + +--- + +## EXECUTION FLOW + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1: Check Status +### ═══════════════════════════════════════════════════════════════ + +#### 1.1: Run Status Command + +```bash +eureka deploy status --verbose +``` + +#### 1.2: Display Results + +The command will show: +- Current deployment status (pending, building, deploying, deployed, failed) +- Version information +- Environment +- Timestamps +- Deployment URL (if deployed) + +--- + +## ARGUMENTS + +| Argument | Default | Description | +|----------|---------|-------------| +| `--verbose` | `false` | Show detailed logs | + +## EXAMPLES + +```bash +# Check current deployment status +/eureka:deploy-status + +# Check with verbose output +/eureka:deploy-status --verbose +``` diff --git a/.claude/commands/eureka/deploy.md b/.claude/commands/eureka/deploy.md new file mode 100644 index 0000000..367d564 --- /dev/null +++ b/.claude/commands/eureka/deploy.md @@ -0,0 +1,421 @@ +--- +description: Deploy application to Eureka platform (creates app if needed) +allowed-tools: Read, Write, Edit, Bash, Glob, Grep +--- + +# Eureka Deploy + +**Input**: "$ARGUMENTS" + +--- + +## PURPOSE + +Deploy the current project to the Eureka platform. Automatically detects framework, validates/generates Dockerfile, creates app if needed, then triggers deployment. + +--- + +## ⛔ CRITICAL RULES + +### MUST DO +1. **MUST** detect framework and validate Dockerfile before deployment +2. **MUST** check for existing `app_id` in `.claude/eureka-factory.yaml` first +3. **MUST** create a new app via API if no `app_id` exists +4. **MUST** use `gitea.authenticatedCloneUrl` for git remote (NOT cloneUrl) +5. **MUST** save the new `app_id` to config after creation +6. **MUST** display deployment status after triggering +7. **MUST** consult `.claude/references/dockerfile-templates.md` for Dockerfile generation + +### CANNOT DO +1. **CANNOT** deploy without valid API key +2. **CANNOT** skip app creation if `app_id` is missing +3. **CANNOT** proceed if API calls fail +4. **CANNOT** deploy without valid Dockerfile (generate if missing) +5. **CANNOT** use `gitea.cloneUrl` for git remote (it's Docker internal URL) + +### CRITICAL: GITEA_EXTERNAL_URL + +The backend **MUST** have `GITEA_EXTERNAL_URL` configured for external access: + +``` +Without GITEA_EXTERNAL_URL: + authenticatedCloneUrl = http://TOKEN@gitea:3000/... ← FAILS (Docker internal) + +With GITEA_EXTERNAL_URL: + authenticatedCloneUrl = https://TOKEN@gitea.yourdomain.com/... ← WORKS +``` + +--- + +## EXECUTION FLOW + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1: Configuration Check +### ═══════════════════════════════════════════════════════════════ + +#### 1.1: Display Start Banner + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🚀 EUREKA DEPLOY ║ +╠══════════════════════════════════════════════════════════════╣ +║ Deploying to Eureka Platform... ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### 1.2: Check Configuration + +Read the configuration file: +```bash +# Check if config exists +cat .claude/eureka-factory.yaml 2>/dev/null || cat .claude/eureka-factory.yml 2>/dev/null || echo "NO_CONFIG" +``` + +**Extract from config:** +- `api_key` - Required for all operations +- `app_id` - If exists, skip app creation +- `api_endpoint` - Optional custom endpoint + +#### 1.3: Validate API Key + +If no `api_key` found: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ❌ NO API KEY CONFIGURED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Run `eureka setup` to configure your credentials. ║ +╚══════════════════════════════════════════════════════════════╝ +``` +**STOP EXECUTION** + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 2: Framework Detection & Dockerfile Validation +### ═══════════════════════════════════════════════════════════════ + +#### 2.1: Detect Framework + +Run framework detection script: +```bash +python3 skills/guardrail-orchestrator/scripts/detect_framework.py . --format summary +``` + +**Expected output:** +``` +Framework: Next.js + Prisma +Base Image: node:20-alpine3.18 +Port: 3000 +Requirements: openssl1.1-compat, output: standalone + +Validation Checks: + ✅ Next.js standalone output: OK + ❌ Dockerfile exists: Dockerfile will be generated +``` + +#### 2.2: Validate/Generate Dockerfile + +**If Dockerfile exists:** +- Verify it matches detected framework +- Check for common issues (wrong base image, missing Prisma support) + +**If Dockerfile is missing:** +- Generate from template in `.claude/references/dockerfile-templates.md` +- Match template to detected framework + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🐳 DOCKERFILE STATUS ║ +╠══════════════════════════════════════════════════════════════╣ +║ Framework: Next.js + Prisma ║ +║ Base Image: node:20-alpine3.18 ║ +║ Port: 3000 ║ +║ Status: Generated / Validated ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### 2.3: Framework-Specific Validations + +**For Next.js:** +- Check `next.config.js` has `output: 'standalone'` +- If missing, offer to add it + +**For SPAs (React/Vue/Angular):** +- Check for `nginx.conf` +- If missing, generate SPA routing config + +**For Prisma projects:** +- Verify `prisma/` directory exists +- Check Dockerfile uses `node:20-alpine3.18` with `openssl1.1-compat` + +#### 2.4: Display Framework Summary + +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ FRAMEWORK DETECTION COMPLETE ║ +╠══════════════════════════════════════════════════════════════╣ +║ Detected: Next.js + Prisma ║ +║ Dockerfile: ✅ Valid ║ +║ Config: ✅ output: 'standalone' set ║ +║ Ready to deploy! ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 3: App Creation (if needed) +### ═══════════════════════════════════════════════════════════════ + +#### 3.1: Check for app_id + +If `app_id` exists in config → **SKIP TO PHASE 4** + +If `app_id` is missing: + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📁 CREATING DIRECTORY APP ║ +╠══════════════════════════════════════════════════════════════╣ +║ No app_id found. Creating new app on Eureka... ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### 3.2: Determine App Name + +Use the project directory name as the default app name: +```bash +APP_NAME=$(basename $(pwd)) +echo "App name: $APP_NAME" +``` + +Or use argument if provided: `$ARGUMENTS` as app name + +#### 3.3: Create Repository with Deploy Access via API + +```bash +API_KEY="" +API_ENDPOINT="" + +curl -X POST "${API_ENDPOINT}/v1/repositories/create-with-gitea" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: ${API_KEY}" \ + -d '{ + "name": "", + "description": "", + "type": "other", + "isPrivate": false, + "createDirectoryApp": true + }' +``` + +**Response structure:** +```json +{ + "success": true, + "repository": { "id": "...", "name": "..." }, + "gitea": { + "repoUrl": "http://gitea:3000/ai-apps/project-abc-myapp", + "cloneUrl": "http://gitea:3000/ai-apps/project-abc-myapp.git", + "authenticatedCloneUrl": "https://TOKEN@gitea.yourdomain.com/ai-apps/project-abc-myapp.git", + "deployUser": "deploy-project-abc-myapp", + "accessToken": "e0d1fd7fe75777..." + }, + "directoryApp": { "id": "...", "slug": "my-app", "status": "COMING_SOON" } +} +``` + +#### 3.4: Add Git Remote + +**IMPORTANT**: Use `authenticatedCloneUrl` (NOT `cloneUrl`): + +```bash +git remote add eureka "" +# Example: git remote add eureka "https://TOKEN@gitea.yourdomain.com/ai-apps/myapp.git" +``` + +#### 3.5: Push Code to Repository + +```bash +git add . +git commit -m "Initial commit for Eureka deployment" +git push -u eureka $(git branch --show-current) +``` + +#### 3.6: Save Config + +Extract IDs from API response and update config: + +```yaml +# .claude/eureka-factory.yaml +api_key: +project_id: +repo_id: +app_id: +slug: +``` + +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ REPOSITORY & APP CREATED ║ +╠══════════════════════════════════════════════════════════════╣ +║ App ID: ║ +║ Slug: ║ +║ Repo ID: ║ +║ Git Remote: ║ +║ Saved to: .claude/eureka-factory.yaml ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### 3.7: Get Clone URL for Existing App (Alternative) + +If app exists but git remote not configured: + +```bash +# Get clone URL by app_id +curl -s -X GET "${API_ENDPOINT}/v1/apps/${APP_ID}/git/clone-url" \ + -H "X-API-Key: ${API_KEY}" + +# Or by slug +curl -s -X GET "${API_ENDPOINT}/v1/apps/by-slug/${SLUG}/git/clone-url" \ + -H "X-API-Key: ${API_KEY}" +``` + +Response includes `git.authenticatedCloneUrl` - use this for the remote. + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 4: Trigger Deployment +### ═══════════════════════════════════════════════════════════════ + +#### 4.1: Trigger Deploy + +```bash +# Using eureka CLI +eureka deploy trigger --yes + +# Or direct API call +curl -X POST "${API_ENDPOINT}/v1/apps/${APP_ID}/deployments" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: ${API_KEY}" \ + -d '{"environment": "production"}' +``` + +#### 4.2: Display Deployment Status + +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ DEPLOYMENT TRIGGERED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Deployment ID: ║ +║ Status: PENDING ║ +║ Environment: production ║ +║ Version: ║ +╠══════════════════════════════════════════════════════════════╣ +║ Use `/eureka:deploy-status` to check progress ║ +║ Use `/eureka:deploy-logs` to view logs ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +## ARGUMENTS + +| Argument | Default | Description | +|----------|---------|-------------| +| `[app-name]` | Directory name | Name for new app (only used if creating) | +| `--env ` | `production` | Deployment environment | +| `--branch ` | Current branch | Git branch to deploy | +| `--force` | `false` | Force deploy even if already deploying | + +## EXAMPLES + +```bash +# Deploy current project (creates app if needed) +/eureka:deploy + +# Deploy with custom app name +/eureka:deploy my-awesome-app + +# Deploy specific branch to staging +/eureka:deploy --env staging --branch develop + +# Force redeploy +/eureka:deploy --force +``` + +--- + +## ERROR HANDLING + +### No Configuration +``` +❌ No configuration found. +Run `eureka setup` to configure credentials. +``` + +### App Creation Failed +``` +❌ Failed to create app: +Check your API key and try again. +``` + +### Deployment Failed +``` +❌ Deployment failed: +Use `/eureka:deploy-logs` to see details. +``` + +--- + +## FLOW DIAGRAM + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ /eureka:deploy │ +├─────────────────────────────────────────────────────────────────┤ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ Check Config │ │ +│ └────────┬────────┘ │ +│ │ │ +│ ┌─────────▼─────────┐ │ +│ │ Has API Key? │ │ +│ └─────────┬─────────┘ │ +│ │ │ +│ NO │ YES │ +│ ┌────────────────────┼────────────────────┐ │ +│ ▼ ▼ │ +│ ┌───────────┐ ┌─────────────────┐ │ +│ │ ERROR │ │ Has app_id? │ │ +│ │ No Key │ └────────┬────────┘ │ +│ └───────────┘ │ │ +│ NO │ YES │ +│ ┌───────────────────┼──────────┐ │ +│ ▼ ▼ │ +│ ┌─────────────────┐ ┌──────────────┐ +│ │ Create App │ │ │ +│ │ via API │ │ │ +│ └────────┬────────┘ │ │ +│ │ │ │ +│ ▼ │ │ +│ ┌─────────────────┐ │ │ +│ │ Save app_id │ │ │ +│ │ to Config │ │ │ +│ └────────┬────────┘ │ │ +│ │ │ │ +│ └───────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ Trigger Deploy │ │ +│ └────────┬────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ Show Status │ │ +│ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` diff --git a/.claude/commands/eureka/index.md b/.claude/commands/eureka/index.md new file mode 100644 index 0000000..be575e1 --- /dev/null +++ b/.claude/commands/eureka/index.md @@ -0,0 +1,595 @@ +--- +description: Generate comprehensive project documentation for engineers and non-engineers +allowed-tools: Read, Write, Edit, Bash, Task, TodoWrite, Glob, Grep +--- + +# Eureka Index - Project Documentation Generator + +**Input**: "$ARGUMENTS" + +--- + +## PURPOSE + +Generate comprehensive, dual-audience documentation by analyzing the current project structure using **parallel agent execution**. The output is designed to be understandable for **both engineers and non-engineers**. + +### Documentation Layers + +| Layer | Audience | Content | +|-------|----------|---------| +| Executive Summary | Everyone | Project purpose, value, capabilities | +| Architecture Overview | Everyone | Visual diagrams, technology stack | +| Getting Started | Semi-technical | Setup, basic usage, configuration | +| Feature Guide | Non-engineers | Plain-language feature descriptions | +| API Reference | Engineers | Endpoints, schemas, authentication | +| Component Catalog | Engineers | Props, interfaces, usage examples | +| Data Models | Both | ER diagrams + plain descriptions | +| Glossary | Non-engineers | Technical terms explained | + +--- + +## EXECUTION ARCHITECTURE + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ PARALLEL EXECUTION PIPELINE │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ PHASE 1: PARALLEL ANALYSIS (run_in_background: true) │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ +│ │ Structure │ │ API │ │ Components │ │ Models │ │ +│ │ Analyzer │ │ Analyzer │ │ Analyzer │ │ Analyzer │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └─────┬──────┘ │ +│ │ │ │ │ │ +│ ▼ ▼ ▼ ▼ │ +│ PHASE 2: SYNCHRONIZATION │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Merge & Create Unified Analysis │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ PHASE 3: PARALLEL DOCUMENTATION (run_in_background: true) │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ +│ │ Main Doc │ │ API Docs │ │ Components │ │ Quick │ │ +│ │ Generator │ │ Generator │ │ Generator │ │ Reference │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └─────┬──────┘ │ +│ │ │ │ │ │ +│ ▼ ▼ ▼ ▼ │ +│ PHASE 4: FINALIZATION │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ HTML Generation + Validation + Summary │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## ⛔ CRITICAL RULES + +### MUST DO +1. **MUST** launch analysis agents in parallel using `run_in_background: true` +2. **MUST** wait for all analysis agents before synchronization +3. **MUST** launch documentation agents in parallel after synchronization +4. **MUST** include both technical and non-technical descriptions +5. **MUST** validate generated documentation against actual code + +### CANNOT DO +1. **CANNOT** make up features that don't exist +2. **CANNOT** skip the parallel analysis phase +3. **CANNOT** generate docs without synchronizing analysis results + +--- + +## EXECUTION FLOW + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1: Parallel Analysis +### ═══════════════════════════════════════════════════════════════ + +#### 1.1: Display Start Banner & Setup +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📚 EUREKA INDEX - Parallel Documentation Generator ║ +╠══════════════════════════════════════════════════════════════╣ +║ Launching parallel analysis agents... ║ +║ Output: Dual-audience documentation (Engineer + Non-Engineer)║ +╚══════════════════════════════════════════════════════════════╝ +``` + +```bash +OUTPUT_DIR="${ARGUMENTS:-docs}" +mkdir -p "$OUTPUT_DIR" +echo "📁 Output directory: $OUTPUT_DIR" +``` + +#### 1.2: Launch Parallel Analysis Agents + +**CRITICAL: Launch ALL four agents in a SINGLE message with multiple Task tool calls:** + +``` +Launch these 4 Task agents IN PARALLEL (single message, multiple tool calls): + +┌─────────────────────────────────────────────────────────────────┐ +│ AGENT 1: Structure Analyzer │ +├─────────────────────────────────────────────────────────────────┤ +│ Task tool with: │ +│ subagent_type: "Explore" │ +│ run_in_background: true │ +│ prompt: | │ +│ # PROJECT STRUCTURE ANALYSIS │ +│ │ +│ Analyze the project structure and return findings. │ +│ │ +│ ## Tasks │ +│ 1. Identify project type (package.json, requirements.txt, │ +│ Cargo.toml, go.mod, pom.xml) │ +│ 2. Extract metadata (name, version, description) │ +│ 3. Map directory structure with purposes │ +│ 4. Identify tech stack (language, framework, database) │ +│ 5. List key dependencies with plain English purposes │ +│ │ +│ ## Output Format (YAML) │ +│ ```yaml │ +│ project: │ +│ name: "..." │ +│ version: "..." │ +│ description: "..." │ +│ type: "node|python|rust|go|java|other" │ +│ tech_stack: │ +│ language: "..." │ +│ framework: "..." │ +│ database: "..." │ +│ structure: │ +│ directories: │ +│ - path: "..." │ +│ purpose: "..." │ +│ file_count: N │ +│ dependencies: │ +│ - name: "..." │ +│ purpose: "plain English" │ +│ ``` │ +└─────────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────────┐ +│ AGENT 2: API Analyzer │ +├─────────────────────────────────────────────────────────────────┤ +│ Task tool with: │ +│ subagent_type: "Explore" │ +│ run_in_background: true │ +│ prompt: | │ +│ # API ENDPOINTS ANALYSIS │ +│ │ +│ Find and analyze all API endpoints in the project. │ +│ │ +│ ## Search Patterns │ +│ - Next.js App Router: app/api/**/route.ts │ +│ - Next.js Pages: pages/api/**/*.ts │ +│ - Express: router.get/post/put/delete │ +│ - FastAPI: @app.get/post/put/delete │ +│ - GraphQL: Query/Mutation resolvers │ +│ │ +│ ## Output Format (YAML) │ +│ ```yaml │ +│ api_endpoints: │ +│ - method: "GET|POST|PUT|DELETE" │ +│ path: "/api/..." │ +│ handler_file: "path/to/file.ts" │ +│ description: "plain English" │ +│ request_body: "schema if POST/PUT" │ +│ response: "schema summary" │ +│ auth_required: true|false │ +│ ``` │ +│ │ +│ If no APIs found, return: api_endpoints: [] │ +└─────────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────────┐ +│ AGENT 3: Components Analyzer │ +├─────────────────────────────────────────────────────────────────┤ +│ Task tool with: │ +│ subagent_type: "Explore" │ +│ run_in_background: true │ +│ prompt: | │ +│ # UI COMPONENTS ANALYSIS │ +│ │ +│ Find and analyze all UI components in the project. │ +│ │ +│ ## Search Patterns │ +│ - React: components/**/*.tsx, function Component() │ +│ - Vue: components/**/*.vue, + + + ``` + + ### Visual Design Standards + + **Hero Section**: + - Full viewport height or 80vh minimum + - Gradient or subtle pattern background + - Large, bold headline (48-72px) + - Clear visual hierarchy + - Floating elements or subtle animation + + **Feature Cards**: + - Icon + Title + Description + - Subtle hover effects (scale, shadow) + - Consistent spacing (24-32px gaps) + - 3-column on desktop, 1 on mobile + + **How It Works**: + - Visual step indicators (1, 2, 3) + - Connecting lines or arrows + - Icons or illustrations per step + - Horizontal on desktop, vertical on mobile + + **FAQ Accordion**: + - Click to expand/collapse + - Smooth animation (max-height transition) + - Plus/minus or chevron indicator + - Category grouping + + **Micro-interactions**: + - Button hover: scale(1.02) + shadow + - Card hover: translateY(-4px) + shadow + - Smooth scroll for anchor links + - Fade-in on scroll (intersection observer) + + ### CSS Requirements + + ```css + /* CSS Custom Properties from branding.json */ + :root { + --color-primary: ...; + --color-secondary: ...; + --font-heading: ...; + --radius: ...; + } + + /* Dark mode */ + @media (prefers-color-scheme: dark) { + :root { + --color-bg: var(--color-bg-dark); + ... + } + } + + /* Responsive breakpoints */ + /* Mobile: < 640px */ + /* Tablet: 640-1024px */ + /* Desktop: > 1024px */ + ``` + + ### JavaScript Requirements + - FAQ accordion functionality + - Smooth scroll for navigation + - Optional: Scroll-triggered animations + - Dark mode toggle (optional) + - Mobile menu toggle + + ### Performance + - Single HTML file (no external dependencies) + - Inline critical CSS + - Minimal JavaScript + - Optimized for Core Web Vitals + - < 100KB total size + + Write complete HTML to: $DOCS_DIR/landing.html +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 4: Finalization +### ═══════════════════════════════════════════════════════════════ + +#### 4.1: Validate Generated Files + +Verify all files exist: +- `$DOCS_DIR/branding.json` +- `$DOCS_DIR/content.json` +- `$DOCS_DIR/faq.json` +- `$DOCS_DIR/seo.json` +- `$DOCS_DIR/landing.html` + +#### 4.2: Display Completion Banner + +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ LANDING PAGE GENERATED SUCCESSFULLY ║ +╠══════════════════════════════════════════════════════════════╣ +║ Output Directory: $DOCS_DIR ║ +╠══════════════════════════════════════════════════════════════╣ +║ Files Created: ║ +║ 🎨 branding.json (Colors, typography, style guide) ║ +║ 📝 content.json (Hero, features, how-it-works) ║ +║ ❓ faq.json (Q&A content by category) ║ +║ 🔍 seo.json (Meta tags, Open Graph, Schema) ║ +║ 🌐 landing.html (Designer-quality landing page) ║ +╠══════════════════════════════════════════════════════════════╣ +║ Landing Page Features: ║ +║ ✅ Hero with compelling headline + CTAs ║ +║ ✅ Feature grid with icons ║ +║ ✅ How It Works visual flow ║ +║ ✅ Interactive FAQ accordion ║ +║ ✅ Responsive (mobile-first) ║ +║ ✅ Dark mode support ║ +║ ✅ SEO optimized ║ +║ ✅ Single file, no dependencies ║ +╠══════════════════════════════════════════════════════════════╣ +║ Next Steps: ║ +║ → Open landing.html in browser to preview ║ +║ → Customize colors in branding.json ║ +║ → Add real screenshots/images ║ +║ → Deploy to your hosting ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +## ARGUMENTS + +| Argument | Default | Description | +|----------|---------|-------------| +| `[docs_dir]` | `docs` | Directory containing documentation from /eureka:index | +| `--style` | `modern` | Design style: modern, minimal, bold, corporate | +| `--theme` | `auto` | Color theme: auto, light, dark | + +## EXAMPLES + +```bash +# Generate landing page from default docs directory +/eureka:landing + +# Generate from custom documentation directory +/eureka:landing my-docs + +# Generate with specific style +/eureka:landing --style minimal + +# Generate dark-only theme +/eureka:landing --theme dark +``` + +--- + +## DESIGN STYLE OPTIONS + +### Modern (Default) +- Gradient backgrounds +- Rounded corners (12-16px) +- Soft shadows +- Vibrant accent colors +- Floating elements + +### Minimal +- Clean white space +- Thin borders +- Muted colors +- Typography-focused +- Subtle interactions + +### Bold +- Strong colors +- Large typography +- High contrast +- Geometric shapes +- Impactful CTAs + +### Corporate +- Professional blues/grays +- Structured layout +- Trust indicators +- Clean lines +- Conservative animations + +--- + +## OUTPUT STRUCTURE + +``` +docs/ +├── analysis.yml (from /eureka:index) +├── PROJECT_DOCUMENTATION.md +├── API_REFERENCE.md +├── COMPONENTS.md +├── QUICK_REFERENCE.md +├── index.html (documentation HTML) +│ +├── branding.json (NEW - concept branding) +├── content.json (NEW - marketing content) +├── faq.json (NEW - Q&A content) +├── seo.json (NEW - SEO metadata) +└── landing.html (NEW - landing page) +``` + +--- + +## BRANDING SYSTEM + +The generated branding.json provides a complete design system: + +```json +{ + "brand": { + "name": "Project Name", + "tagline": "Your compelling tagline", + "value_proposition": "What makes it unique" + }, + "colors": { + "primary": "#6366f1", + "secondary": "#8b5cf6", + "accent": "#f59e0b", + "background": "#ffffff", + "background_dark": "#0f172a", + "text": "#1e293b", + "text_muted": "#64748b" + }, + "typography": { + "heading_font": "Inter, system-ui, sans-serif", + "body_font": "Inter, system-ui, sans-serif", + "mono_font": "JetBrains Mono, Consolas, monospace" + }, + "style": { + "border_radius": "12px", + "shadow_sm": "0 1px 2px rgba(0,0,0,0.05)", + "shadow_md": "0 4px 6px -1px rgba(0,0,0,0.1)", + "shadow_lg": "0 10px 15px -3px rgba(0,0,0,0.1)", + "gradient": "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)" + } +} +``` + +This can be used to: +- Maintain consistent branding across all pages +- Generate CSS custom properties +- Create Figma/design tool exports +- Build component libraries diff --git a/.claude/commands/guardrail/analyze.md b/.claude/commands/guardrail/analyze.md new file mode 100644 index 0000000..4620c31 --- /dev/null +++ b/.claude/commands/guardrail/analyze.md @@ -0,0 +1,243 @@ +--- +description: Analyze codebase and generate project manifest from existing code +allowed-tools: Read, Write, Bash, Glob, Grep, Task, AskUserQuestion +--- + +# Analyze Codebase and Generate Manifest + +Analyze the current codebase and generate all files needed for the guardrail workflow system. + +## Arguments + +- `$ARGUMENTS` - Optional flags: + - `--force` - Overwrite existing manifest without asking + - `--dry-run` - Preview manifest without writing + - `--deep` - Use AI agent for deeper analysis + - `` - Custom project name + +## Generated Files + +This command creates: +1. `project_manifest.json` - Entity definitions and dependencies +2. `.workflow/index.yml` - Version tracking index +3. `.workflow/versions/` - Directory for version snapshots + +## Quick Execution (Default) + +### Step 1: Run the Python analyzer script +```bash +python3 skills/guardrail-orchestrator/scripts/analyze_codebase.py --path . $ARGUMENTS +``` + +If the script succeeds, continue to Step 2. + +### Step 2: Initialize Workflow Directory Structure [MANDATORY] +```bash +# Create workflow directory structure +mkdir -p .workflow/versions + +# Create index.yml if it doesn't exist +if [ ! -f .workflow/index.yml ]; then + cat > .workflow/index.yml << 'EOF' +versions: [] +latest_version: null +total_versions: 0 +EOF + echo "Created .workflow/index.yml" +fi +``` + +### Step 3: Display Summary +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ GUARDRAIL INITIALIZED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Files Created: ║ +║ ✓ project_manifest.json ║ +║ ✓ .workflow/index.yml ║ +║ ✓ .workflow/versions/ ║ +╠══════════════════════════════════════════════════════════════╣ +║ Ready to use: ║ +║ /workflow:spawn Start a new feature ║ +║ /guardrail:status Check project status ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +## Deep Analysis Mode (--deep flag) + +**Use the Task tool to spawn an Explore agent for comprehensive codebase analysis** + +Use Task tool with: + subagent_type: "Explore" + prompt: | + Analyze this codebase thoroughly and return structured information about: + + 1. **Pages** (Next.js App Router): + - Find all page.tsx files in app/ directory + - Extract route paths from file locations + - Identify components imported/used + - Identify API dependencies (fetch calls) + + 2. **Components**: + - Find all .tsx files in app/components/ + - Extract component names and exports + - Extract prop interfaces/types + - Identify child component dependencies + + 3. **API Routes**: + - Find all route.ts files in app/api/ + - Extract HTTP methods (GET, POST, PUT, DELETE, PATCH) + - Identify request/response types + - Extract path parameters + + 4. **Database/Types**: + - Find type definitions in app/lib/ + - Extract interfaces and type aliases + - Identify database schemas/tables + + 5. **Dependencies**: + - Which components are used by which pages + - Which APIs are called by which components + - Which database tables are used by which APIs + + Return the analysis as structured JSON sections. + +### Phase 2: Generate Manifest + +Based on the analysis, create `project_manifest.json` with this structure: + +```json +{ + "project": { + "name": "", + "version": "1.0.0", + "created_at": "", + "description": "" + }, + "state": { + "current_phase": "IMPLEMENTATION_PHASE", + "approval_status": { + "manifest_approved": true, + "approved_by": "analyzer", + "approved_at": "" + }, + "revision_history": [ + { + "action": "MANIFEST_GENERATED", + "timestamp": "", + "details": "Generated from existing codebase analysis" + } + ] + }, + "entities": { + "pages": [ + { + "id": "page_", + "path": "/", + "file_path": "app//page.tsx", + "status": "IMPLEMENTED", + "description": "", + "components": ["comp_", ...], + "data_dependencies": ["api_", ...] + } + ], + "components": [ + { + "id": "comp_", + "name": "", + "file_path": "app/components/.tsx", + "status": "IMPLEMENTED", + "description": "", + "props": { + "": { + "type": "", + "optional": true|false, + "description": "" + } + } + } + ], + "api_endpoints": [ + { + "id": "api__", + "path": "/api/", + "method": "GET|POST|PUT|DELETE|PATCH", + "file_path": "app/api//route.ts", + "status": "IMPLEMENTED", + "description": "", + "request": { + "params": {}, + "query": {}, + "body": {} + }, + "response": { + "type": "", + "description": "" + } + } + ], + "database_tables": [ + { + "id": "table_", + "name": "", + "file_path": "app/lib/db.ts", + "status": "IMPLEMENTED", + "description": "", + "columns": {} + } + ] + }, + "dependencies": { + "component_to_page": {}, + "api_to_component": {}, + "table_to_api": {} + }, + "types": {} +} +``` + +### Phase 3: Entity Naming Conventions + +Use these ID formats: +- **Pages**: `page_` (e.g., `page_home`, `page_tasks`, `page_task_detail`) +- **Components**: `comp_` (e.g., `comp_task_list`, `comp_filter_bar`) +- **APIs**: `api__` (e.g., `api_list_tasks`, `api_create_task`) +- **Tables**: `table_` (e.g., `table_tasks`, `table_users`) + +### Phase 4: Write Manifest + +1. Write the generated manifest to `project_manifest.json` +2. Validate JSON syntax +3. Display summary: + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📊 MANIFEST GENERATED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Project: ║ +║ Generated: ║ +╠══════════════════════════════════════════════════════════════╣ +║ ENTITIES DISCOVERED ║ +║ 📄 Pages: X ║ +║ 🧩 Components: X ║ +║ 🔌 APIs: X ║ +║ 🗄️ Tables: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ Status: All entities marked as IMPLEMENTED ║ +║ Phase: IMPLEMENTATION_PHASE ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +## Options + +If manifest already exists, ask user: +1. **Overwrite** - Replace existing manifest +2. **Merge** - Add new entities, keep existing +3. **Cancel** - Abort operation + +## Notes + +- All discovered entities are marked as `IMPLEMENTED` since they already exist +- Project starts in `IMPLEMENTATION_PHASE` since code exists +- Use this command to bring existing projects under guardrail management +- After generation, use `/guardrail:validate` to verify manifest accuracy diff --git a/.claude/commands/guardrail/approve.md b/.claude/commands/guardrail/approve.md new file mode 100644 index 0000000..1a04a9a --- /dev/null +++ b/.claude/commands/guardrail/approve.md @@ -0,0 +1,47 @@ +--- +description: Approve design and transition to IMPLEMENTATION_PHASE (Reviewer mode) +allowed-tools: Read, Write, Bash +--- + +# Approve Design (Reviewer Mode) + +✅ **REVIEWER MODE ACTIVATED** + +Approve the current design and enable implementation. + +## CRITICAL RULES + +You are acting as the **REVIEWER AGENT**. + +✅ **ALLOWED**: +- Read any file +- Update approval status in manifest +- Transition phases + +❌ **BLOCKED**: +- Write ANY code files +- You cannot implement anything + +## Steps + +1. **Verify Phase**: Must be in `DESIGN_REVIEW` + +2. **Run Full Validation**: +```bash +python3 "$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_manifest.py" --strict +``` + +3. **If Valid**, update manifest: + - Set `state.approval_status.manifest_approved = true` + - Set `state.approval_status.approved_by = "reviewer"` + - Set `state.approval_status.approved_at = ` + +4. **Transition to Implementation**: +```bash +python3 "$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/transition_phase.py" --to IMPLEMENTATION_PHASE +``` + +5. **Show Results**: + - List all entities now with status `APPROVED` + - Explain that code can now be written for these entities + - Suggest `/guardrail:implement` to start diff --git a/.claude/commands/guardrail/design.md b/.claude/commands/guardrail/design.md new file mode 100644 index 0000000..e5138aa --- /dev/null +++ b/.claude/commands/guardrail/design.md @@ -0,0 +1,81 @@ +--- +description: Design a new feature by adding entities to manifest (Architect mode) +allowed-tools: Read, Write, Bash +--- + +# Design Feature (Architect Mode) + +🏗️ **ARCHITECT MODE ACTIVATED** + +Design the feature: "$ARGUMENTS" + +## CRITICAL RULES + +You are now acting as the **ARCHITECT AGENT**. + +✅ **ALLOWED**: +- Read any file +- Write to `project_manifest.json` ONLY +- Run validation scripts + +❌ **BLOCKED**: +- Write ANY code files (.ts, .tsx, .css, .sql, .js, .jsx) +- You CANNOT write implementation code yet + +## Workflow + +1. **Verify Phase**: Check `project_manifest.json` - must be in `DESIGN_PHASE` + +2. **Analyze Requirements**: Break down "$ARGUMENTS" into: + - Pages needed (routes/screens) + - Components needed (UI elements) + - API endpoints needed (backend routes) + - Database tables needed (data storage) + +3. **Define Each Entity** in manifest with: + - Unique ID following naming convention + - Complete specification (props, schemas, columns) + - `status: "DEFINED"` + - File path where it will be implemented + +4. **Update Manifest**: Add all entities to `project_manifest.json` + +5. **Validate**: Run `python3 skills/guardrail-orchestrator/scripts/validate_manifest.py` + +6. **Summarize**: List what was added and suggest `/guardrail:review` + +## Entity Templates + +### Page +```json +{ + "id": "page_[name]", + "path": "/[route]", + "file_path": "src/pages/[name]/index.tsx", + "status": "DEFINED", + "components": [], + "data_dependencies": [] +} +``` + +### Component +```json +{ + "id": "comp_[name]", + "name": "[PascalCase]", + "file_path": "src/components/[Name]/index.tsx", + "status": "DEFINED", + "props": {} +} +``` + +### API Endpoint +```json +{ + "id": "api_[action]_[resource]", + "path": "/api/v1/[resource]", + "method": "GET|POST|PUT|DELETE", + "file_path": "src/api/[resource]/[action].ts", + "status": "DEFINED" +} +``` diff --git a/.claude/commands/guardrail/implement.md b/.claude/commands/guardrail/implement.md new file mode 100644 index 0000000..cb151e3 --- /dev/null +++ b/.claude/commands/guardrail/implement.md @@ -0,0 +1,74 @@ +--- +description: Implement an approved entity from the manifest +allowed-tools: Read, Write, Bash +--- + +# Implement Entity + +Implement the entity: "$ARGUMENTS" + +## CRITICAL RULES + +⚠️ **GUARDRAIL ENFORCEMENT ACTIVE** + +You can ONLY write to files that: +1. Are defined in `project_manifest.json` +2. Have status = `APPROVED` +3. Match the `file_path` in the manifest EXACTLY + +## Steps + +1. **Verify Phase**: Must be in `IMPLEMENTATION_PHASE` + +2. **Find Entity** in manifest: + - If "$ARGUMENTS" is `--all`: implement all APPROVED entities + - Otherwise: find the specific entity by ID + +3. **For Each Entity**: + + a. **Load Definition** from manifest + + b. **Verify Status** is `APPROVED` + + c. **Generate Code** matching the specification: + - Props must match manifest exactly + - Types must match manifest exactly + - File path must match `file_path` in manifest + + d. **Write File** to the exact path in manifest + + e. **Run Validations**: + ```bash + npm run lint --if-present + npm run type-check --if-present + ``` + +4. **Status Updates** (handled by post-hook): + - Entity status changes to `IMPLEMENTED` + - Timestamp recorded + +## Code Templates + +### Component (Frontend) +```tsx +import React from 'react'; + +interface [Name]Props { + // From manifest.props +} + +export const [Name]: React.FC<[Name]Props> = (props) => { + return ( + // Implementation + ); +}; +``` + +### API Endpoint (Backend) +```typescript +import { Request, Response } from 'express'; + +export async function handler(req: Request, res: Response) { + // From manifest.request/response schemas +} +``` diff --git a/.claude/commands/guardrail/init.md b/.claude/commands/guardrail/init.md new file mode 100644 index 0000000..94db4f3 --- /dev/null +++ b/.claude/commands/guardrail/init.md @@ -0,0 +1,67 @@ +--- +description: Initialize a new guardrailed project with manifest +allowed-tools: Bash, Write, Read +--- + +# Initialize Guardrailed Project + +Initialize a new project called "$ARGUMENTS" with guardrail enforcement and workflow system. + +## Generated Files + +This command creates: +1. `project_manifest.json` - Entity definitions and dependencies +2. `.workflow/index.yml` - Version tracking index +3. `.workflow/versions/` - Directory for version snapshots + +## Steps + +### Step 1: Run the initialization script +```bash +python3 skills/guardrail-orchestrator/scripts/init_project.py --name "$ARGUMENTS" --path . +``` + +### Step 2: Initialize Workflow Directory Structure [MANDATORY] +```bash +# Create workflow directory structure +mkdir -p .workflow/versions + +# Create index.yml if it doesn't exist +if [ ! -f .workflow/index.yml ]; then + cat > .workflow/index.yml << 'EOF' +versions: [] +latest_version: null +total_versions: 0 +EOF +fi +``` + +### Step 3: Verify and Display Summary +```bash +# Verify files exist +ls project_manifest.json .workflow/index.yml +``` + +Display: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ PROJECT INITIALIZED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Project: $ARGUMENTS ║ +║ Phase: DESIGN_PHASE ║ +╠══════════════════════════════════════════════════════════════╣ +║ Files Created: ║ +║ ✓ project_manifest.json ║ +║ ✓ .workflow/index.yml ║ +║ ✓ .workflow/versions/ ║ +╠══════════════════════════════════════════════════════════════╣ +║ Next Steps: ║ +║ /guardrail:design Design features in manifest ║ +║ /workflow:spawn Start automated workflow ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +## Notes +- Project starts in **DESIGN_PHASE** (manifest edits only) +- Use `/guardrail:design` for manual design workflow +- Use `/workflow:spawn` for automated design + implementation diff --git a/.claude/commands/guardrail/review.md b/.claude/commands/guardrail/review.md new file mode 100644 index 0000000..908610d --- /dev/null +++ b/.claude/commands/guardrail/review.md @@ -0,0 +1,32 @@ +--- +description: Request design review and transition to DESIGN_REVIEW phase +allowed-tools: Read, Write, Bash +--- + +# Request Design Review + +Transition the project from DESIGN_PHASE to DESIGN_REVIEW. + +## Steps + +1. **Validate Manifest**: +```bash +python3 "$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_manifest.py" --strict +``` + +2. **If Valid**, transition phase: +```bash +python3 "$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/transition_phase.py" --to DESIGN_REVIEW +``` + +3. **Show Review Checklist**: + - [ ] All pages have at least one component + - [ ] All components have defined props with types + - [ ] All APIs have request/response schemas + - [ ] All database tables have primary keys + - [ ] No orphan components + - [ ] No circular dependencies + +4. **Explain Next Steps**: + - Use `/guardrail:approve` to approve and move to implementation + - Use `/guardrail:reject ` to send back for fixes diff --git a/.claude/commands/guardrail/status.md b/.claude/commands/guardrail/status.md new file mode 100644 index 0000000..53ba37e --- /dev/null +++ b/.claude/commands/guardrail/status.md @@ -0,0 +1,27 @@ +--- +description: Show current project phase, entity counts, and pending work +allowed-tools: Read, Bash +--- + +# Guardrail Status + +Display the current guardrail project status. + +## Steps + +1. Read `project_manifest.json` + +2. Display: + - **Current Phase**: `state.current_phase` + - **Approval Status**: `state.approval_status.manifest_approved` + - **Entity Counts**: + - Pages: count by status (DEFINED/APPROVED/IMPLEMENTED) + - Components: count by status + - API Endpoints: count by status + - Database Tables: count by status + - **Recent History**: last 5 items from `state.revision_history` + +3. Show available actions for current phase: + - DESIGN_PHASE: Can use `/guardrail:design`, then `/guardrail:review` + - DESIGN_REVIEW: Can use `/guardrail:approve` or `/guardrail:reject` + - IMPLEMENTATION_PHASE: Can use `/guardrail:implement` diff --git a/.claude/commands/guardrail/validate.md b/.claude/commands/guardrail/validate.md new file mode 100644 index 0000000..4467e50 --- /dev/null +++ b/.claude/commands/guardrail/validate.md @@ -0,0 +1,29 @@ +--- +description: Validate manifest integrity and completeness +allowed-tools: Bash, Read +--- + +# Validate Manifest + +Run validation checks on `project_manifest.json`. + +## Command + +```bash +python3 "$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_manifest.py" $ARGUMENTS +``` + +## Options + +- No arguments: Basic validation +- `--strict`: Treat warnings as errors + +## What It Checks + +1. **Structure**: Required top-level keys exist +2. **Pages**: Have paths, components, file_paths +3. **Components**: Have props with types, valid dependencies +4. **APIs**: Have methods, paths, request/response schemas +5. **Database**: Tables have primary keys, valid foreign keys +6. **Dependencies**: No orphans, no circular references +7. **Naming**: Follows conventions diff --git a/.claude/commands/guardrail/verify.md b/.claude/commands/guardrail/verify.md new file mode 100644 index 0000000..e6d03e1 --- /dev/null +++ b/.claude/commands/guardrail/verify.md @@ -0,0 +1,57 @@ +--- +description: Verify implementation matches manifest specifications +allowed-tools: Bash, Read +--- + +# Verify Implementation + +Run verification to ensure all implemented code matches the manifest specifications. + +## Command + +```bash +python3 "$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/verify_implementation.py" --project-root "$CLAUDE_PROJECT_DIR" $ARGUMENTS +``` + +## Options + +- No arguments: Basic verification +- `--verbose` or `-v`: Include warnings +- `--json`: Output as JSON for programmatic use + +## What It Checks + +For each entity in the manifest: + +### Components +- File exists at specified `file_path` +- Component name is exported +- Props interface matches manifest definition + +### Pages +- File exists at specified `file_path` +- Has `export default` (Next.js requirement) +- Uses specified component dependencies + +### API Endpoints +- File exists at specified `file_path` +- HTTP method handler exists (GET, POST, PUT, DELETE) +- Request parameters are handled + +### Database Tables +- File exists at specified `file_path` +- Column definitions present +- CRUD operations implemented + +## Example Output + +``` +✅ [component] comp_button + File: app/components/Button.tsx + +❌ [component] comp_missing + File: app/components/Missing.tsx + ❌ ERROR: File not found + +SUMMARY: 17/18 passed, 1 failed, 3 warnings +``` diff --git a/.claude/commands/workflow/approve.md b/.claude/commands/workflow/approve.md new file mode 100644 index 0000000..8bedd42 --- /dev/null +++ b/.claude/commands/workflow/approve.md @@ -0,0 +1,87 @@ +--- +description: Approve a workflow gate (design or implementation) +allowed-tools: Read, Write, Bash +--- + +# Approve Workflow Gate + +Approve gate: "$ARGUMENTS" + +## Valid Gates +- `design` - Approve the design phase (entities + tasks) +- `implementation` - Approve the implementation phase (all code) + +## Steps + +### 1. Validate Gate +- If "$ARGUMENTS" is not `design` or `implementation`: STOP and show usage + +### 2. Check Workflow State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py exists +``` + +If no active workflow: +``` +❌ No active workflow found. + Start a new workflow with: /workflow:spawn "feature name" +``` + +### 3. Verify Current Phase + +**For design approval**: +- Current phase must be `AWAITING_DESIGN_APPROVAL` +- If not: Report error with current phase + +**For implementation approval**: +- Current phase must be `AWAITING_IMPL_APPROVAL` +- If not: Report error with current phase + +### 4. Execute Approval + +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py approve $ARGUMENTS +``` + +### 5. Transition to Next Phase + +**If design approved**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition DESIGN_APPROVED +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPLEMENTING +``` + +**If implementation approved**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPL_APPROVED +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition COMPLETING +``` + +### 6. Report + +**Design Approved**: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ DESIGN APPROVED ║ +╠══════════════════════════════════════════════════════════════╣ +║ The design has been approved. Implementation will begin. ║ +║ ║ +║ Next steps: ║ +║ /workflow:frontend --next Start frontend tasks ║ +║ /workflow:backend --next Start backend tasks ║ +║ /workflow:resume Auto-continue workflow ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +**Implementation Approved**: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ IMPLEMENTATION APPROVED ║ +╠══════════════════════════════════════════════════════════════╣ +║ All implementations have been approved. ║ +║ ║ +║ Next steps: ║ +║ /workflow:complete --all Mark all tasks as done ║ +║ /workflow:resume Auto-complete workflow ║ +╚══════════════════════════════════════════════════════════════╝ +``` diff --git a/.claude/commands/workflow/backend.md b/.claude/commands/workflow/backend.md new file mode 100644 index 0000000..bcd4563 --- /dev/null +++ b/.claude/commands/workflow/backend.md @@ -0,0 +1,86 @@ +--- +description: Implement backend tasks (Backend agent) +allowed-tools: Read, Write, Edit, Bash +agents: backend-implementer, workflow-validator +--- + +# Backend Agent - Implementation Mode + +⚙️ **BACKEND AGENT ACTIVATED** + +Implement task: "$ARGUMENTS" + +## CRITICAL RULES + +You are now the **BACKEND AGENT**. + +✅ **ALLOWED**: +- Read any file +- Write new files (API routes, DB) +- Edit existing backend files +- Run Bash (build, lint, type-check, tests) + +✅ **ALLOWED FILES**: +- `app/api/**/*` +- `app/lib/**/*` +- `prisma/**/*` +- `db/**/*` + +## Workflow + +### Step 1: Load Task +First, get the version-specific tasks directory: +```bash +TASKS_DIR=$(python3 skills/guardrail-orchestrator/scripts/version_manager.py tasks-dir) +``` + +Read the task file: `$TASKS_DIR/$ARGUMENTS.yml` +- If "$ARGUMENTS" is `--next`: find first task with `agent: backend` and `status: pending` + +### Step 2: Update Workflow State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task in_progress +``` + +### Step 3: Verify Prerequisites +- Check entity is `APPROVED` in `project_manifest.json` +- Check all `dependencies` tasks are `completed` +- If blocked: + ```bash + python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task blocked + ``` + Stop and report blocker. + +### Step 4: Implement +For each file in `file_paths`: +1. Read manifest entity specification +2. Generate code matching spec exactly: + - HTTP methods must match manifest + - Request params must match manifest + - Response types must match manifest +3. Follow existing project patterns + +### Step 5: Validate +Run validations: +```bash +npm run lint +npm run build +``` + +### Step 6: Update Task Status +Update the task file: +```yaml +status: review +completed_at: +``` + +Update workflow state: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task review +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py progress --tasks-impl +``` + +### Step 7: Report +- List implemented files +- Show validation results +- Suggest: `/workflow:review $ARGUMENTS` diff --git a/.claude/commands/workflow/complete.md b/.claude/commands/workflow/complete.md new file mode 100644 index 0000000..e9923d8 --- /dev/null +++ b/.claude/commands/workflow/complete.md @@ -0,0 +1,66 @@ +--- +description: Mark approved task as completed +allowed-tools: Read, Write, Bash +--- + +# Complete Task + +Mark task "$ARGUMENTS" as completed. + +## Prerequisites +- Task must have `status: approved` +- All acceptance criteria verified by reviewer + +## Steps + +### 1. Read Task +First, get the version-specific tasks directory: +```bash +TASKS_DIR=$(python3 skills/guardrail-orchestrator/scripts/version_manager.py tasks-dir) +``` + +Read `$TASKS_DIR/$ARGUMENTS.yml` + +### 2. Verify Status +- If `status` is NOT `approved`: STOP and report error +- If `status` is `approved`: proceed + +### 3. Update Task +Update the task file with: +```yaml +status: completed +completed_at: +``` + +### 4. Update Workflow State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task completed +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py progress --tasks-completed +``` + +### 5. Update Manifest (if applicable) +For each entity in `entity_ids`: +- Update entity status to `IMPLEMENTED` in `project_manifest.json` + +### 6. Check Workflow Completion +Check if all tasks are now completed: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` + +If all tasks completed, transition to implementation approval: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition AWAITING_IMPL_APPROVAL +``` + +### 7. Report +``` +✅ Task completed: $ARGUMENTS + +Entities implemented: + - + - + +Next: /workflow:status to see remaining tasks + /workflow:complete --all to complete all approved tasks +``` diff --git a/.claude/commands/workflow/design.md b/.claude/commands/workflow/design.md new file mode 100644 index 0000000..5d0e306 --- /dev/null +++ b/.claude/commands/workflow/design.md @@ -0,0 +1,585 @@ +--- +description: Design system architecture with ER diagram, API contracts, and UI structure +allowed-tools: Read, Write, Edit, Bash, Task, TodoWrite +agents: workflow-reviewer, type-generator +--- + +# Workflow Design - System Architecture Phase + +**Input**: "$ARGUMENTS" + +--- + +## PURPOSE + +This command creates a comprehensive **design document** that serves as the source of truth for implementation. It defines: + +1. **Data Layer** - ER diagram with models, fields, relations +2. **API Layer** - REST endpoints with request/response contracts +3. **UI Layer** - Pages and components with data requirements +4. **Dependency Graph** - Layered execution order for parallel tasks + +--- + +## ⛔ CRITICAL RULES + +### MUST DO +1. **MUST** create `design_document.yml` with ALL layers defined +2. **MUST** run `validate_design.py` to generate dependency graph +3. **MUST** verify no circular dependencies before proceeding +4. **MUST** show layered execution plan to user + +### CANNOT DO +1. **CANNOT** create tasks without design document +2. **CANNOT** skip validation step +3. **CANNOT** proceed if validation fails + +--- + +## EXECUTION FLOW + +### ═══════════════════════════════════════════════════════════════ +### STEP 1: Initialize Design Session +### ═══════════════════════════════════════════════════════════════ + +#### 1.1: Get Current Version +```bash +# Get active version from workflow +VERSION_ID=$(cat .workflow/current.yml 2>/dev/null | grep "active_version:" | awk '{print $2}') +if [ -z "$VERSION_ID" ]; then + echo "ERROR: No active workflow. Run /workflow:spawn first" + exit 1 +fi +echo "VERSION_ID=$VERSION_ID" +``` + +#### 1.2: Create Design Directory +```bash +mkdir -p .workflow/versions/$VERSION_ID/design +mkdir -p .workflow/versions/$VERSION_ID/contexts +mkdir -p .workflow/versions/$VERSION_ID/tasks +``` + +#### 1.3: Display Design Start Banner +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📐 SYSTEM DESIGN PHASE ║ +╠══════════════════════════════════════════════════════════════╣ +║ Feature: $ARGUMENTS ║ +║ Version: $VERSION_ID ║ +╠══════════════════════════════════════════════════════════════╣ +║ You will define: ║ +║ Layer 1: Data Models (ER Diagram) ║ +║ Layer 2: API Endpoints (REST Contracts) ║ +║ Layer 3: Pages & Components (UI Structure) ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### STEP 2: Analyze Requirements & Design System +### ═══════════════════════════════════════════════════════════════ + +**Use Task tool with system-architect agent:** + +``` +Use Task tool with: + subagent_type: "system-architect" + prompt: | + # SYSTEM ARCHITECT - Design Document Creation + + ## INPUT + Feature: "$ARGUMENTS" + Version: $VERSION_ID + Output: .workflow/versions/$VERSION_ID/design/design_document.yml + + ## YOUR MISSION + Create a comprehensive design document following the schema in: + skills/guardrail-orchestrator/schemas/design_document.yml + + ## ANALYSIS PROCESS + + ### Phase A: Understand Requirements + 1. Read the feature description carefully + 2. Identify the core user flows + 3. Determine what data needs to be stored + 4. Identify what APIs are needed + 5. Plan the UI structure + + ### Phase B: Design Data Layer (ER Diagram) + For each entity needed: + - Define fields with types and constraints + - Define relations to other entities + - Define validations + - Consider indexes for performance + + ### Phase C: Design API Layer + For each endpoint needed: + - Define HTTP method and path + - Define request body schema (for POST/PUT/PATCH) + - Define response schemas for all status codes + - Define authentication requirements + - Link to data models used + + ### Phase D: Design UI Layer + For each page needed: + - Define route path + - List data requirements (which APIs) + - List components used + - Define auth requirements + + For each component needed: + - Define props interface + - Define events emitted + - List child components + - List APIs called directly (if any) + + ## OUTPUT FORMAT + + Create `.workflow/versions/$VERSION_ID/design/design_document.yml`: + + ```yaml + # Design Document + workflow_version: "$VERSION_ID" + feature: "$ARGUMENTS" + created_at: + status: draft + revision: 1 + + # LAYER 1: DATA MODELS + data_models: + - id: model_ + name: + description: "" + table_name: + fields: + - name: id + type: uuid + constraints: [primary_key] + - name: + type: + constraints: [] + # If enum: + enum_values: [, ] + relations: + - type: + target: model_ + foreign_key: + on_delete: + timestamps: true + validations: + - field: + rule: "" + message: "" + + # LAYER 2: API ENDPOINTS + api_endpoints: + - id: api__ + method: + path: /api/ + summary: "" + description: "" + # For POST/PUT/PATCH: + request_body: + content_type: application/json + schema: + type: object + properties: + - name: + type: + required: + validations: [] + responses: + - status: 200 + description: "Success" + schema: + type: object + properties: + - name: + type: + - status: 400 + description: "Validation error" + schema: + type: object + properties: + - name: error + type: string + depends_on_models: [model_] + auth: + required: + roles: [, ] + + # LAYER 3: PAGES + pages: + - id: page_ + name: "" + path: / + layout: + data_needs: + - api_id: api_ + purpose: "" + on_load: + components: [component_, component_] + auth: + required: + roles: [] + redirect: /login + + # LAYER 3: COMPONENTS + components: + - id: component_ + name: + props: + - name: + type: + required: + description: "" + events: + - name: + payload: "" + description: "" + uses_apis: [] + uses_components: [component_] + variants: [, ] + ``` + + ## DESIGN PRINCIPLES + + 1. **Start with Data**: What data is needed? Design models first. + 2. **APIs Serve UI**: What operations does UI need? Design APIs next. + 3. **UI Consumes APIs**: Pages/Components use APIs. Design UI last. + 4. **Explicit Dependencies**: Every relation must be clearly defined. + 5. **Contracts First**: API request/response schemas are contracts. + + ## VERIFICATION + + After creating the design document, verify: + 1. Every API references existing models + 2. Every page references existing APIs and components + 3. Every component references existing child components + 4. No circular dependencies + + ## OUTPUT + + After creating the file, output: + ``` + === DESIGN DOCUMENT CREATED === + + Data Models: X + API Endpoints: X + Pages: X + Components: X + + File: .workflow/versions/$VERSION_ID/design/design_document.yml + ``` +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### STEP 3: Validate Design & Generate Dependency Graph +### ═══════════════════════════════════════════════════════════════ + +#### 3.1: Run Design Validation [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/validate_design.py \ + .workflow/versions/$VERSION_ID/design/design_document.yml \ + --output-dir .workflow/versions/$VERSION_ID +``` + +**This generates:** +- `.workflow/versions/$VERSION_ID/dependency_graph.yml` - Layered execution order +- `.workflow/versions/$VERSION_ID/contexts/*.yml` - Per-entity context snapshots +- `.workflow/versions/$VERSION_ID/tasks/*.yml` - Tasks with full context + +#### 3.2: Check Validation Result +```bash +VALIDATION_EXIT=$? +if [ $VALIDATION_EXIT -ne 0 ]; then + echo "❌ Design validation failed. Fix errors and re-run." + exit 1 +fi +``` + +**BLOCK IF**: Validation fails → Display errors, do not proceed + +--- + +### ═══════════════════════════════════════════════════════════════ +### STEP 4: Generate ASCII Page Visualizations +### ═══════════════════════════════════════════════════════════════ + +**For each page in the design, generate an ASCII wireframe visualization:** + +Read the design document and for each page, display an ASCII visualization: + +``` +╔══════════════════════════════════════════════════════════════════════════╗ +║ 📄 PAGE VISUALIZATIONS ║ +╠══════════════════════════════════════════════════════════════════════════╣ +║ ║ +║ Page: /songs (page_songs_list) ║ +║ ──────────────────────────────────────────────────────────────────── ║ +║ ║ +║ ┌──────────────────────────────────────────────────────────────┐ ║ +║ │ 🔍 SearchBar [component_search_bar] │ ║ +║ │ ┌─────────────────────────────────────┐ ┌──────────────┐ │ ║ +║ │ │ Search songs... │ │ Search │ │ ║ +║ │ └─────────────────────────────────────┘ └──────────────┘ │ ║ +║ └──────────────────────────────────────────────────────────────┘ ║ +║ ║ +║ ┌──────────────────────────────────────────────────────────────┐ ║ +║ │ 📋 SongList [component_song_list] │ ║ +║ │ │ ║ +║ │ ┌────────────────────────────────────────────────────────┐ │ ║ +║ │ │ 🎵 SongCard [component_song_card] │ │ ║ +║ │ │ ┌───────┐ │ │ ║ +║ │ │ │ 🖼️ │ Song Title │ │ ║ +║ │ │ │ cover │ Artist Name [▶️] [➕] │ │ ║ +║ │ │ └───────┘ │ │ ║ +║ │ └────────────────────────────────────────────────────────┘ │ ║ +║ │ │ ║ +║ │ ┌────────────────────────────────────────────────────────┐ │ ║ +║ │ │ 🎵 SongCard [component_song_card] │ │ ║ +║ │ │ ... │ │ ║ +║ │ └────────────────────────────────────────────────────────┘ │ ║ +║ └──────────────────────────────────────────────────────────────┘ ║ +║ ║ +║ Data: api_get_songs (on_load: true) ║ +║ Events: onPlay → api_play_song, onAddToPlaylist → shows modal ║ +║ ║ +╠══════════════════════════════════════════════════════════════════════════╣ +║ ║ +║ Page: /songs/[id] (page_song_detail) ║ +║ ──────────────────────────────────────────────────────────────────── ║ +║ ║ +║ ┌──────────────────────────────────────────────────────────────┐ ║ +║ │ 🎵 SongHeader [component_song_header] │ ║ +║ │ ┌─────────────┐ │ ║ +║ │ │ │ Song Title │ ║ +║ │ │ 🖼️ Cover │ by Artist Name │ ║ +║ │ │ (large) │ Album: Album Name │ ║ +║ │ │ │ Duration: 3:45 │ ║ +║ │ └─────────────┘ │ ║ +║ │ [▶️ Play] [➕ Add to Playlist] │ ║ +║ └──────────────────────────────────────────────────────────────┘ ║ +║ ║ +║ ┌──────────────────────────────────────────────────────────────┐ ║ +║ │ 📝 LyricsSection [component_lyrics] │ ║ +║ │ │ ║ +║ │ Verse 1: │ ║ +║ │ Lorem ipsum dolor sit amet... │ ║ +║ │ │ ║ +║ └──────────────────────────────────────────────────────────────┘ ║ +║ ║ +║ ┌──────────────────────────────────────────────────────────────┐ ║ +║ │ 🎵 RelatedSongs [component_related_songs] │ ║ +║ │ Similar songs you might like: │ ║ +║ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ ║ +║ │ │ Song 1 │ │ Song 2 │ │ Song 3 │ │ ║ +║ │ └─────────┘ └─────────┘ └─────────┘ │ ║ +║ └──────────────────────────────────────────────────────────────┘ ║ +║ ║ +║ Data: api_get_song (on_load: true), api_get_related_songs ║ +║ ║ +╚══════════════════════════════════════════════════════════════════════════╝ +``` + +**Visualization Guidelines:** + +For each page in design_document.yml: +1. **Draw page container** with route path and page_id +2. **Draw each component** as a box with: + - Component name and component_id + - Visual representation of what it contains + - Event handlers shown as buttons [▶️] [➕] +3. **Show component hierarchy** with nesting +4. **List data requirements** at the bottom: + - Which APIs are called + - on_load triggers + - User action triggers + +**Component Visual Patterns:** + +| Component Type | ASCII Pattern | +|---------------|---------------| +| Card | `┌──────┐` with content inside | +| Button | `[Label]` | +| Input | `┌────────────────┐` empty box | +| List | Multiple cards stacked | +| Grid | Cards side by side | +| Image | `🖼️` emoji placeholder | +| Icon | Appropriate emoji | + +--- + +### ═══════════════════════════════════════════════════════════════ +### STEP 5: Display Layered Execution Plan +### ═══════════════════════════════════════════════════════════════ + +Read the generated dependency graph and display: + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📊 DEPENDENCY GRAPH - EXECUTION LAYERS ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ Layer 1: DATA MODELS (Parallel) ║ +║ ───────────────────────────────────────────── ║ +║ 📦 model_user → backend ║ +║ 📦 model_post → backend ║ +║ ║ +║ Layer 2: API ENDPOINTS (Parallel, after Layer 1) ║ +║ ───────────────────────────────────────────── ║ +║ 🔌 api_create_user → backend (needs: model_user) ║ +║ 🔌 api_list_users → backend (needs: model_user) ║ +║ 🔌 api_create_post → backend (needs: model_user, model_post)║ +║ ║ +║ Layer 3: UI (Parallel, after Layer 2) ║ +║ ───────────────────────────────────────────── ║ +║ 🧩 component_user_card → frontend ║ +║ 📄 page_users → frontend (needs: api_list_users) ║ +║ ║ +╠══════════════════════════════════════════════════════════════╣ +║ EXECUTION SUMMARY ║ +║ Total entities: X ║ +║ Total layers: X ║ +║ Max parallelism: X (tasks can run simultaneously) ║ +║ Critical path: X layers deep ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### STEP 6: Display Design Summary for Approval +### ═══════════════════════════════════════════════════════════════ + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🛑 DESIGN APPROVAL REQUIRED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Feature: $ARGUMENTS ║ +║ Version: $VERSION_ID ║ +╠══════════════════════════════════════════════════════════════╣ +║ DESIGN DOCUMENT ║ +║ 📦 Data Models: X ║ +║ 🔌 API Endpoints: X ║ +║ 📄 Pages: X ║ +║ 🧩 Components: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ GENERATED ARTIFACTS ║ +║ ✅ Dependency graph calculated ║ +║ ✅ Context snapshots created (X files) ║ +║ ✅ Implementation tasks created (X tasks) ║ +╠══════════════════════════════════════════════════════════════╣ +║ EXECUTION PLAN ║ +║ Layer 1: X tasks (parallel) → backend ║ +║ Layer 2: X tasks (parallel) → backend ║ +║ Layer 3: X tasks (parallel) → frontend ║ +╠══════════════════════════════════════════════════════════════╣ +║ FILES CREATED ║ +║ .workflow/versions/$VERSION_ID/design/design_document.yml ║ +║ .workflow/versions/$VERSION_ID/dependency_graph.yml ║ +║ .workflow/versions/$VERSION_ID/contexts/*.yml ║ +║ .workflow/versions/$VERSION_ID/tasks/*.yml ║ +╠══════════════════════════════════════════════════════════════╣ +║ NEXT STEPS ║ +║ Review the design above, then: ║ +║ /workflow:approve - Proceed to implementation ║ +║ /workflow:reject - Request design changes ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### STEP 7: Transition Workflow State +### ═══════════════════════════════════════════════════════════════ + +```bash +# Update progress +TASK_COUNT=$(ls .workflow/versions/$VERSION_ID/tasks/*.yml 2>/dev/null | wc -l) +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py progress \ + --tasks-created $TASK_COUNT + +# Transition to awaiting approval +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition AWAITING_DESIGN_APPROVAL +``` + +--- + +## CONTEXT SNAPSHOT EXAMPLE + +Each task gets a context file like `.workflow/versions/$VERSION_ID/contexts/api_create_user.yml`: + +```yaml +task_id: task_create_api_create_user +entity_id: api_create_user +workflow_version: v001 + +target: + type: api + definition: + method: POST + path: /api/users + request_body: + properties: + - name: email + type: string + required: true + validations: [email] + - name: password + type: string + required: true + validations: [min:8] + responses: + - status: 201 + schema: { id, email, name, created_at } + - status: 400 + schema: { error, details } + - status: 409 + schema: { error } + +related: + models: + - id: model_user + definition: + name: User + fields: + - { name: id, type: uuid } + - { name: email, type: string } + - { name: password_hash, type: string } + +dependencies: + entity_ids: [model_user] + +files: + to_create: + - app/api/users/route.ts + reference: + - path: app/api/health/route.ts + purpose: "API route pattern" + +acceptance: + - criterion: "POST /api/users returns 201 on success" + verification: "curl -X POST /api/users with valid data" + - criterion: "Returns 409 if email exists" + verification: "Test with duplicate email" +``` + +--- + +## USAGE + +```bash +# After /workflow:spawn, run design: +/workflow:design + +# This will: +# 1. Create comprehensive design document +# 2. Validate and generate dependency graph +# 3. Create tasks with full context +# 4. Wait for approval before implementation +``` diff --git a/.claude/commands/workflow/diff.md b/.claude/commands/workflow/diff.md new file mode 100644 index 0000000..d24800d --- /dev/null +++ b/.claude/commands/workflow/diff.md @@ -0,0 +1,193 @@ +--- +description: Compare workflow versions and show manifest changes +allowed-tools: Read, Bash +--- + +# Workflow Version Diff + +Compare workflow versions to see what changed in the project manifest. + +## EXECUTION PROTOCOL + +### Step 1: Parse Arguments + +``` +IF "$ARGUMENTS" = "": + MODE = "current" (diff latest version with current) +ELSE IF "$ARGUMENTS" matches "v\d+ v\d+": + MODE = "versions" (diff two specific versions) +ELSE IF "$ARGUMENTS" matches "v\d+": + MODE = "single" (diff specific version with current) +ELSE IF "$ARGUMENTS" = "--changelog" or "--log": + MODE = "changelog" (show all version changelogs) +ELSE IF "$ARGUMENTS" contains "--json": + OUTPUT = "json" +``` + +### Step 2: Get Available Versions + +```bash +python3 skills/guardrail-orchestrator/scripts/manifest_diff.py versions +``` + +### Step 3: Execute Diff Based on Mode + +**MODE: current (default)** +```bash +# Get latest version +LATEST=$(ls -1 .workflow/versions/ 2>/dev/null | tail -1) + +# Diff with current manifest +python3 skills/guardrail-orchestrator/scripts/manifest_diff.py diff $LATEST current +``` + +**MODE: versions (e.g., "v001 v002")** +```bash +# Diff two specific versions +python3 skills/guardrail-orchestrator/scripts/manifest_diff.py diff v001 v002 +``` + +**MODE: single (e.g., "v001")** +```bash +# Diff specific version with current +python3 skills/guardrail-orchestrator/scripts/manifest_diff.py diff v001 +``` + +**MODE: changelog** +```bash +# Show all changelogs +python3 skills/guardrail-orchestrator/scripts/manifest_diff.py changelog +``` + +**JSON output** +```bash +# Add --json for programmatic use +python3 skills/guardrail-orchestrator/scripts/manifest_diff.py diff v001 --json +``` + +### Step 4: Display Results + +The script outputs: + +``` +╔══════════════════════════════════════════════════════════════════════╗ +║ MANIFEST DIFF: v001 → v002 ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ SUMMARY ║ +║ + Added: 3 ║ +║ ~ Modified: 2 ║ +║ - Removed: 1 ║ +║ = Unchanged: 5 ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ BY TYPE ║ +║ pages: +1 ║ +║ components: +2 ~1 ║ +║ api_endpoints: ~1 -1 ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ ➕ ADDED ║ +║ + 📄 Profile (app/profile/page.tsx) ║ +║ + 🧩 Button (app/components/Button.tsx) ║ +║ + 🧩 Modal (app/components/Modal.tsx) ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ 📝 MODIFIED ║ +║ ~ 🧩 Header (app/components/Header.tsx) ║ +║ dependencies: [] → ['Button'] ║ +║ ~ 🔌 users (app/api/users/route.ts) ║ +║ status: PENDING → IMPLEMENTED ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ ➖ REMOVED ║ +║ - 🔌 legacy (app/api/legacy/route.ts) ║ +╚══════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## USAGE EXAMPLES + +```bash +# Compare latest version with current manifest +/workflow:diff + +# Compare two specific versions +/workflow:diff v001 v002 + +# Compare specific version with current +/workflow:diff v003 + +# Show all version changelogs +/workflow:diff --changelog + +# Output as JSON +/workflow:diff v001 --json +``` + +--- + +## WHAT IT SHOWS + +### Entity Changes +- **Added**: New pages, components, API endpoints, etc. +- **Modified**: Status changes, dependency updates, path changes +- **Removed**: Deleted entities from manifest + +### Entity Type Icons +- 📄 page +- 🧩 component +- 🔌 api_endpoint +- 📚 lib +- 🪝 hook +- 📝 type +- ⚙️ config + +### Change Details +- Entity name and file path +- Specific field changes with before/after values +- Summary statistics by type + +--- + +## CHANGELOG MODE + +Show version history with changes: + +```bash +/workflow:diff --changelog +``` + +Output: +``` +╔══════════════════════════════════════════════════════════════════════╗ +║ CHANGELOG: v001 ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ Feature: User authentication ║ +║ Status: completed ║ +║ Started: 2025-01-15 10:30:00 ║ +║ Completed: 2025-01-15 14:45:00 ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ CHANGES ║ +║ + Added page: Login ║ +║ + Added page: Register ║ +║ + Added component: AuthForm ║ +║ + Added api_endpoint: auth ║ +╚══════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## INTEGRATION + +### Uses Version Snapshots + +The diff tool uses snapshots created by version_manager.py: +- `snapshot_before/manifest.json` - Manifest at version start +- `snapshot_after/manifest.json` - Manifest at version completion + +These are automatically created when: +- `/workflow:spawn` initializes a new version +- `/workflow:complete` marks a version as done + +### Related Commands + +- `/workflow:history` - List all workflow versions +- `/workflow:status` - Show current workflow state +- `/workflow:changelog ` - Alias for `--changelog` diff --git a/.claude/commands/workflow/frontend.md b/.claude/commands/workflow/frontend.md new file mode 100644 index 0000000..c340734 --- /dev/null +++ b/.claude/commands/workflow/frontend.md @@ -0,0 +1,86 @@ +--- +description: Implement frontend tasks (Frontend agent) +allowed-tools: Read, Write, Edit, Bash +agents: frontend-implementer, workflow-validator, type-generator +--- + +# Frontend Agent - Implementation Mode + +🎨 **FRONTEND AGENT ACTIVATED** + +Implement task: "$ARGUMENTS" + +## CRITICAL RULES + +You are now the **FRONTEND AGENT**. + +✅ **ALLOWED**: +- Read any file +- Write new files (components, pages) +- Edit existing UI files +- Run Bash (build, lint, type-check) + +✅ **ALLOWED FILES**: +- `app/components/**/*` +- `app/**/page.tsx` +- `app/**/layout.tsx` +- `app/globals.css` + +## Workflow + +### Step 1: Load Task +First, get the version-specific tasks directory: +```bash +TASKS_DIR=$(python3 skills/guardrail-orchestrator/scripts/version_manager.py tasks-dir) +``` + +Read the task file: `$TASKS_DIR/$ARGUMENTS.yml` +- If "$ARGUMENTS" is `--next`: find first task with `agent: frontend` and `status: pending` + +### Step 2: Update Workflow State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task in_progress +``` + +### Step 3: Verify Prerequisites +- Check entity is `APPROVED` in `project_manifest.json` +- Check all `dependencies` tasks are `completed` +- If blocked: + ```bash + python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task blocked + ``` + Stop and report blocker. + +### Step 4: Implement +For each file in `file_paths`: +1. Read manifest entity specification +2. Generate code matching spec exactly: + - Props must match manifest + - Types must match manifest + - File path must match manifest +3. Follow existing project patterns + +### Step 5: Validate +Run validations: +```bash +npm run lint +npm run build +``` + +### Step 6: Update Task Status +Update the task file: +```yaml +status: review +completed_at: +``` + +Update workflow state: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task review +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py progress --tasks-impl +``` + +### Step 7: Report +- List implemented files +- Show validation results +- Suggest: `/workflow:review $ARGUMENTS` diff --git a/.claude/commands/workflow/history.md b/.claude/commands/workflow/history.md new file mode 100644 index 0000000..d434888 --- /dev/null +++ b/.claude/commands/workflow/history.md @@ -0,0 +1,94 @@ +--- +description: Show workflow version history +allowed-tools: Read, Bash +--- + +# Workflow History + +Display version history of all workflow sessions. + +## Usage +``` +/workflow:history # List all versions +/workflow:history v001 # Show details for specific version +/workflow:history --changelog # Show changelog for current version +``` + +## Steps + +### 1. List All Versions (default) +```bash +python3 skills/guardrail-orchestrator/scripts/version_manager.py history +``` + +### 2. Show Version Details +If "$ARGUMENTS" is a version (e.g., `v001`): +```bash +python3 skills/guardrail-orchestrator/scripts/version_manager.py changelog $ARGUMENTS +``` + +### 3. Display Format + +**Version List**: +``` +╔══════════════════════════════════════════════════════════════════════╗ +║ WORKFLOW VERSION HISTORY ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ ✅ v003: Dashboard with analytics ║ +║ Started: 2025-01-16T16:00:00 | Tasks: 12 | Ops: 45 ║ +║ ────────────────────────────────────────────────────────────────── ║ +║ ✅ v002: Task filters and search ║ +║ Started: 2025-01-16T14:00:00 | Tasks: 8 | Ops: 28 ║ +║ ────────────────────────────────────────────────────────────────── ║ +║ ✅ v001: User authentication ║ +║ Started: 2025-01-16T10:00:00 | Tasks: 5 | Ops: 18 ║ +╚══════════════════════════════════════════════════════════════════════╝ +``` + +**Version Changelog**: +``` +╔══════════════════════════════════════════════════════════════════════╗ +║ CHANGELOG: v001 ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ Feature: User authentication ║ +║ Status: completed ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ CREATED ║ +║ + [page] page_login ║ +║ app/login/page.tsx ║ +║ + [component] component_LoginForm ║ +║ app/components/LoginForm.tsx ║ +║ + [api] api_auth ║ +║ app/api/auth/route.ts ║ +║ UPDATED ║ +║ ~ [component] component_Header ║ +║ DELETED ║ +║ (none) ║ +╠══════════════════════════════════════════════════════════════════════╣ +║ SUMMARY ║ +║ Entities: +3 ~1 -0 ║ +║ Files: +4 ~2 -0 ║ +╚══════════════════════════════════════════════════════════════════════╝ +``` + +### 4. Show Task Sessions +If `$ARGUMENTS` includes `--tasks`: +List all task sessions for the version with their operations: + +``` +Task Sessions for v001: +───────────────────────────────────────────────── +🎨 task_create_LoginPage (frontend) + Status: completed | Duration: 5m 32s + Operations: + + CREATE file: app/login/page.tsx + ~ UPDATE manifest: project_manifest.json + Review: ✅ approved + +⚙️ task_create_AuthAPI (backend) + Status: completed | Duration: 8m 15s + Operations: + + CREATE file: app/api/auth/route.ts + + CREATE file: app/lib/auth.ts + Review: ✅ approved +``` diff --git a/.claude/commands/workflow/reject.md b/.claude/commands/workflow/reject.md new file mode 100644 index 0000000..9eec98c --- /dev/null +++ b/.claude/commands/workflow/reject.md @@ -0,0 +1,113 @@ +--- +description: Reject a workflow gate and request changes +allowed-tools: Read, Write, Bash +--- + +# Reject Workflow Gate + +Reject gate with reason: "$ARGUMENTS" + +## Usage +``` +/workflow:reject design "Need more API endpoints for authentication" +/workflow:reject implementation "Login form missing validation" +``` + +## Steps + +### 1. Parse Arguments +Extract: +- `gate`: First word (design | implementation) +- `reason`: Remaining text in quotes + +If invalid format: +``` +❌ Usage: /workflow:reject "reason" + + Examples: + /workflow:reject design "Need user profile page" + /workflow:reject implementation "Missing error handling" +``` + +### 2. Check Workflow State +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py exists +``` + +If no active workflow: +``` +❌ No active workflow found. +``` + +### 3. Verify Current Phase + +**For design rejection**: +- Current phase must be `AWAITING_DESIGN_APPROVAL` + +**For implementation rejection**: +- Current phase must be `AWAITING_IMPL_APPROVAL` + +### 4. Execute Rejection + +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py reject "" +``` + +### 5. Transition to Revision Phase + +**If design rejected**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition DESIGN_REJECTED +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition DESIGNING +``` + +**If implementation rejected**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPL_REJECTED +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPLEMENTING +``` + +### 6. Report + +**Design Rejected**: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ❌ DESIGN REJECTED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Reason: ║ +║ ║ +║ The workflow has returned to the DESIGNING phase. ║ +║ Revision count: X ║ +║ ║ +║ Next steps: ║ +║ /workflow:design --revise Revise the design ║ +║ /workflow:resume Auto-revise and continue ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +**Implementation Rejected**: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ❌ IMPLEMENTATION REJECTED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Reason: ║ +║ ║ +║ The workflow has returned to the IMPLEMENTING phase. ║ +║ Revision count: X ║ +║ ║ +║ Tasks requiring fixes will be marked as 'pending'. ║ +║ ║ +║ Next steps: ║ +║ /workflow:frontend --next Fix frontend tasks ║ +║ /workflow:backend --next Fix backend tasks ║ +║ /workflow:resume Auto-fix and continue ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +### 7. Update Related Tasks (Implementation Rejection) + +If implementation was rejected, identify tasks related to the rejection reason and mark them as pending: + +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task pending +``` diff --git a/.claude/commands/workflow/resume.md b/.claude/commands/workflow/resume.md new file mode 100644 index 0000000..bb728da --- /dev/null +++ b/.claude/commands/workflow/resume.md @@ -0,0 +1,241 @@ +--- +description: Resume an interrupted workflow from saved state +allowed-tools: Read, Write, Edit, Bash, AskUserQuestion, TodoWrite +agents: workflow-orchestrator, workflow-validator +--- + +# Workflow Orchestrator - Resume + +Resume a previously interrupted or paused workflow. + +## EXECUTION PROTOCOL + +### Step 1: Load Workflow State + +Read `.workflow/current.yml`: +- If not found: Report "No workflow to resume" and exit +- If found: Load state and continue + +### Step 2: Display Resume Summary + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🔄 RESUMING WORKFLOW ║ +╠══════════════════════════════════════════════════════════════╣ +║ Workflow ID: ║ +║ Feature: ║ +║ Started: ║ +║ Last Updated: ║ +╠══════════════════════════════════════════════════════════════╣ +║ CURRENT STATE ║ +║ Phase: ║ +║ Resume Point: ║ +╠══════════════════════════════════════════════════════════════╣ +║ PROGRESS ║ +║ Entities Designed: ║ +║ Tasks Created: ║ +║ Tasks Implemented: ║ +║ Tasks Reviewed: ║ +║ Tasks Completed: ║ +╠══════════════════════════════════════════════════════════════╣ +║ LAST ERROR (if any): ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +### Step 3: Confirm Resume + +**Ask user**: +- Option 1: "Continue - Resume from current point" +- Option 2: "Restart Phase - Redo current phase from beginning" +- Option 3: "Abort - Cancel workflow entirely" + +### Step 4: Resume Based on Phase + +**INITIALIZING**: +→ Continue to DESIGNING phase + +**DESIGNING**: +→ Continue architect work +→ Resume creating entities/tasks + +**AWAITING_DESIGN_APPROVAL**: +→ Present design summary again +→ Ask for approval + +**DESIGN_APPROVED**: +→ **CRITICAL**: Generate types before implementing +→ Run: `python3 skills/guardrail-orchestrator/scripts/generate_types.py .workflow/versions/$VERSION_ID/design/design_document.yml --output-dir types` +→ Verify types exist: `ls types/types.ts types/component-props.ts types/api-types.ts` +→ Transition to IMPLEMENTING + +**DESIGN_REJECTED**: +→ Show rejection reason +→ Return to DESIGNING with feedback + +**IMPLEMENTING**: +→ **Step 1**: Verify generated types exist + ```bash + ls types/types.ts types/component-props.ts types/api-types.ts + ``` + If missing → Run type generation first + +→ **Step 2**: Display type compliance reminder + ``` + ╔══════════════════════════════════════════════════════════════╗ + ║ ⚠️ TYPE COMPLIANCE REQUIRED ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ ALL implementations MUST: ║ + ║ 1. Import types from @/types ║ + ║ 2. Import component props from @/types/component-props ║ + ║ 3. Use object props (not flat props) ║ + ║ 4. Implement ALL events from design ║ + ╚══════════════════════════════════════════════════════════════╝ + ``` + +→ **Step 3**: Find incomplete tasks +→ **Step 4**: Continue implementation from next pending task +→ **Step 5**: Run validation after completing tasks: + ```bash + python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist + ``` + +**REVIEWING**: +→ Find tasks awaiting review +→ Continue review process + +**SECURITY_REVIEW**: +→ Continue security scanning +→ Run: `python3 skills/guardrail-orchestrator/scripts/security_scan.py --project-dir . --severity HIGH` +→ Run: `python3 skills/guardrail-orchestrator/scripts/validate_api_contract.py --project-dir .` +→ If passed: Transition to AWAITING_IMPL_APPROVAL +→ If critical issues: Return to IMPLEMENTING with security feedback + +**AWAITING_IMPL_APPROVAL**: +→ Present implementation summary again +→ Ask for approval + +**IMPL_APPROVED**: +→ Continue to COMPLETING phase + +**IMPL_REJECTED**: +→ Show rejection reason +→ Return to IMPLEMENTING with feedback + +**COMPLETING**: +→ Continue marking tasks complete + +**PAUSED**: +→ Resume from `resume_point.phase` + +**FAILED**: +→ Show error details +→ Ask user how to proceed: + - Retry failed operation + - Skip and continue + - Abort workflow + +### Step 5: Continue Workflow + +Execute remaining phases following `/workflow:spawn` protocol. + +## TASK-LEVEL RESUME + +If resuming during IMPLEMENTING phase: + +1. **Verify types exist**: + ```bash + ls types/types.ts types/component-props.ts types/api-types.ts types/index.ts + ``` + If missing → Generate types first: + ```bash + python3 skills/guardrail-orchestrator/scripts/generate_types.py \ + .workflow/versions/$VERSION_ID/design/design_document.yml \ + --output-dir types + ``` + +2. **Verify IMPLEMENTATION_CONTEXT.md exists**: + ```bash + ls .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md + ``` + If missing → Regenerate (see spawn.md Step 3.5.4) + +3. **Display context reminder to agent**: + ``` + ╔══════════════════════════════════════════════════════════════╗ + ║ 🔴 MANDATORY CONTEXT READING ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ BEFORE implementing ANY task, you MUST read: ║ + ║ ║ + ║ 1. .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md ║ + ║ Contains: All rules, types, import patterns ║ + ║ ║ + ║ 2. .workflow/versions/$VERSION_ID/tasks/task_.yml ║ + ║ Contains: Task definition, acceptance criteria ║ + ║ ║ + ║ 3. .workflow/versions/$VERSION_ID/contexts/.yml ║ + ║ Contains: Entity context, props, events, schemas ║ + ║ ║ + ║ 4. types/component-props.ts OR types/api-types.ts ║ + ║ Contains: Generated types - SOURCE OF TRUTH ║ + ╚══════════════════════════════════════════════════════════════╝ + ``` + +4. **Identify incomplete tasks**: + ```yaml + # Resume from first task not in 'completed' or 'approved' + resume_task: tasks.pending[0] || tasks.in_progress[0] || tasks.review[0] + ``` + +5. **Skip completed work**: + - Don't recreate files that exist and are valid + - Don't re-run validations that passed + +6. **Continue from failure point**: + - If task failed mid-implementation, restart that task + - If validation failed, show error and retry + +7. **For each task, remind agent of type requirements**: + ``` + ⚠️ IMPLEMENTATION RULES: + - READ .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md FIRST + - Import types from @/types (NOT define your own) + - Import props from @/types/component-props + - Use object props: { song: Song } NOT { id, title, ... } + - Implement ALL events from design + ``` + +8. **Run validation after completing tasks**: + ```bash + python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist + ``` + - If errors found → Fix before proceeding + - If warnings only → Continue with caution + +## STATE RECOVERY + +If `.workflow/current.yml` is corrupted: + +1. **Check for backup**: `.workflow/current.yml.bak` +2. **Attempt recovery from manifest**: + - Read `project_manifest.json` for entity status + - Scan version-specific tasks directory for task status + - Reconstruct workflow state +3. **If unrecoverable**: + - Report error + - Suggest starting fresh with `/workflow:spawn` + +## ABORT WORKFLOW + +If user chooses to abort: + +1. **Confirm abort**: + "This will cancel the workflow. Files already created will remain. Continue?" + +2. **If confirmed**: + - Archive state to `.workflow/history/_aborted.yml` + - Clear `.workflow/current.yml` + - Report: "Workflow aborted. Created files remain in place." + +3. **Cleanup options**: + - Offer to rollback created files (if git available) + - Offer to keep partial implementation diff --git a/.claude/commands/workflow/review.md b/.claude/commands/workflow/review.md new file mode 100644 index 0000000..07f4874 --- /dev/null +++ b/.claude/commands/workflow/review.md @@ -0,0 +1,569 @@ +--- +description: Review implementation (Reviewer agent) +allowed-tools: Read, Bash +agents: workflow-reviewer, workflow-validator +--- + +# Reviewer Agent - Review Mode + +**Input**: "$ARGUMENTS" + +--- + +## CRITICAL ENFORCEMENT RULES + +**YOU ARE IN READ-ONLY MODE. VIOLATIONS WILL BE BLOCKED.** + +### MUST DO (Non-Negotiable) +1. **MUST** run all validation checks (build, typecheck, lint, test, API contract) +2. **MUST** verify every file in task's `file_paths` exists +3. **MUST** read and analyze each implemented file +4. **MUST** check against acceptance_criteria in task file +5. **MUST** output structured review report (format below) +6. **MUST** run workflow_manager.py to update task status + +### CANNOT DO (Strictly Forbidden) +1. **CANNOT** create files (Write tool blocked) +2. **CANNOT** modify files (Edit tool blocked) +3. **CANNOT** fix issues yourself - only report them +4. **CANNOT** approve tasks with missing files +5. **CANNOT** approve if ANY validation check fails +6. **CANNOT** skip any validation check + +### ALLOWED ACTIONS +- Read any file +- Run Bash commands (build, lint, test, typecheck, ls, cat, grep) +- Output review reports +- Update task status via workflow_manager.py + +--- + +## VALIDATION CHECKS MATRIX + +| Check | Command | Blocks Approval | When | +|-------|---------|-----------------|------| +| Build | `npm run build` | YES | Always | +| TypeScript | `npx tsc --noEmit` | YES | Always | +| Async/Await | `python3 verify_async.py` | YES | Always | +| Lint | `npm run lint` | YES (if --strict) | --strict mode | +| Unit Tests | `npm test -- --passWithNoTests` | YES (if --strict) | --strict mode | +| API Contract | `python3 validate_api_contract.py` | YES | Always | +| **Relations** | `python3 validate_relations.py` | YES | Always | +| Security Scan | `python3 security_scan.py` | YES (critical) | Always | +| Files Exist | `ls -la` each file | YES | Always | + +**Note:** For comprehensive security audit, use `/workflow:security --full` + +--- + +## ARGUMENT PARSING + +``` +IF "$ARGUMENTS" contains "--auto": + MODE = AUTO (batch review all tasks) + STRICT = "$ARGUMENTS" contains "--strict" + FULL = "$ARGUMENTS" contains "--full" +ELSE IF "$ARGUMENTS" = "--next": + MODE = SINGLE (next pending task) +ELSE: + MODE = SINGLE (specific task: "$ARGUMENTS") +``` + +--- + +## MODE: AUTO REVIEW (--auto) + +### Step A1: Get Active Version [MANDATORY] +```bash +VERSION_ID=$(python3 skills/guardrail-orchestrator/scripts/version_manager.py current) +TASKS_DIR=".workflow/versions/$VERSION_ID/tasks" +``` + +### Step A2: Run Global Validations [MANDATORY] + +#### 2.1 Build Check +```bash +npm run build 2>&1 +BUILD_EXIT=$? +echo "BUILD_EXIT=$BUILD_EXIT" +``` + +#### 2.2 TypeScript Strict Check +```bash +npx tsc --noEmit 2>&1 +TS_EXIT=$? +echo "TS_EXIT=$TS_EXIT" +``` + +#### 2.3 Async/Await Check [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/verify_async.py --path . 2>&1 +ASYNC_EXIT=$? +echo "ASYNC_EXIT=$ASYNC_EXIT" +``` + +This catches runtime errors at build time: +- `fetch()` without `await` +- `.json()` without `await` +- `Promise.all()` without `await` +- Floating promises (unawaited async calls) + +**Exit codes:** +- 0 = PASS (no high-severity issues) +- 1 = HIGH severity issues found (blocks approval) + +#### 2.4 Lint Check (if --strict or --full) +```bash +npm run lint 2>&1 +LINT_EXIT=$? +echo "LINT_EXIT=$LINT_EXIT" +``` + +#### 2.5 Unit Tests (if --strict or --full) +```bash +npm test -- --passWithNoTests 2>&1 +TEST_EXIT=$? +echo "TEST_EXIT=$TEST_EXIT" +``` + +#### 2.6 API Contract Validation [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/validate_api_contract.py --project-dir . 2>&1 +API_EXIT=$? +echo "API_EXIT=$API_EXIT" +``` + +This validates: +- Frontend API calls have matching backend endpoints +- HTTP methods match (GET, POST, PUT, DELETE) +- Request bodies are sent where expected +- Response handling matches backend output + +#### 2.7 Relationship Chain Validation [MANDATORY] +```bash +# Build relations graph and validate alignment +python3 skills/guardrail-orchestrator/scripts/build_relations.py \ + --project-dir . \ + --output .workflow/relations.yml 2>&1 + +python3 skills/guardrail-orchestrator/scripts/validate_relations.py \ + --project-dir . \ + --relations .workflow/relations.yml 2>&1 +RELATIONS_EXIT=$? +echo "RELATIONS_EXIT=$RELATIONS_EXIT" +``` + +This validates the full data flow chain: +- **Database → API**: API response types match database schema fields +- **API → Component**: Component props match API response types +- **Component → Page**: Pages correctly provide data to components +- **Reference integrity**: All dependencies exist +- **Layer violations**: Lower layers don't depend on higher layers +- **Circular dependencies**: No dependency cycles + +**Exit codes:** +- 0 = PASS (all relationships valid) +- 1 = WARNINGS (type mismatches, missing chains) +- 2 = ERRORS (missing references, circular deps, layer violations) + +#### 2.8 Security Scan [MANDATORY] +```bash +# Run comprehensive security scanner +python3 skills/guardrail-orchestrator/scripts/security_scan.py \ + --project-dir . \ + --severity HIGH +SECURITY_EXIT=$? +echo "SECURITY_EXIT=$SECURITY_EXIT" +``` + +**Security scan checks:** +- Hardcoded secrets (API keys, passwords, tokens) +- SQL injection vulnerabilities +- XSS risks (dangerouslySetInnerHTML, innerHTML) +- Command injection patterns +- Path traversal vulnerabilities +- NoSQL injection risks +- SSRF vulnerabilities +- Prototype pollution +- Insecure authentication patterns +- CORS misconfigurations +- Sensitive data exposure +- Debug code in production + +**Exit codes:** +- 0 = PASS (no critical/high issues) +- 1 = HIGH issues found (warning) +- 2 = CRITICAL issues found (blocks approval) + +**For full security audit, run:** `/workflow:security --full` + +### Step A3: Gather All Tasks [MANDATORY] +```bash +ls $TASKS_DIR/*.yml 2>/dev/null +``` +**MUST process ALL task files found** + +### Step A4: Review Each Task [MANDATORY] + +For EACH task file: + +```bash +# Extract file_paths from task +TASK_FILES=$(grep -A 20 "file_paths:" "$TASK_FILE" | grep -E "^\s+-" | sed 's/.*- //') +``` + +**Check each file exists**: +```bash +for file in $TASK_FILES; do + if [ -f "$file" ]; then + echo "EXISTS: $file" + else + echo "MISSING: $file" + MISSING_COUNT=$((MISSING_COUNT + 1)) + fi +done +``` + +**Determine task verdict**: +``` +IF all files exist + AND BUILD_EXIT = 0 + AND TS_EXIT = 0 + AND ASYNC_EXIT = 0 + AND API_EXIT = 0 + AND RELATIONS_EXIT != 2 (no critical relation errors) + AND SECURITY_EXIT != 2 (no critical security issues) + AND (not --strict OR (LINT_EXIT = 0 AND TEST_EXIT = 0)): + -> TASK_VERDICT = APPROVED +ELSE: + -> TASK_VERDICT = REJECTED + -> Record reason (missing files / build failure / type error / async issue / API mismatch / relation error / security issue) +``` + +**Security exit codes:** +- 0 = PASS +- 1 = HIGH issues (warning, doesn't block unless --strict) +- 2 = CRITICAL issues (always blocks) + +### Step A5: Batch Update [MANDATORY] + +For APPROVED tasks: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task approved +``` + +For REJECTED tasks: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task pending +``` + +### Step A6: Auto Review Report [MANDATORY] + +**MUST output this exact format**: +``` ++======================================================================+ +| REVIEW COMPLETE | ++======================================================================+ +| Version: $VERSION_ID | +| Mode: AUTO [STRICT if --strict] [FULL if --full] | ++======================================================================+ +| VALIDATION RESULTS | ++----------------------------------------------------------------------+ +| Build: PASS (exit 0) / FAIL (exit $BUILD_EXIT) | +| TypeScript: PASS (exit 0) / FAIL (exit $TS_EXIT) | +| Async/Await: PASS / FAIL (X high, Y medium issues) | +| Lint: PASS / FAIL / SKIPPED | +| Tests: PASS / FAIL / SKIPPED | +| API Contract: PASS / FAIL (X errors, Y warnings) | +| Relations: PASS / WARN / FAIL (X errors, Y warnings) | +| Security: PASS / WARNING / CRITICAL | +| (C:X H:X M:X L:X issues) | ++======================================================================+ +| API CONTRACT DETAILS | ++----------------------------------------------------------------------+ +| Frontend calls: X matched, Y unmatched | +| Backend endpoints: X defined, Y unused | +| Method mismatches: X | +| Body mismatches: X | ++======================================================================+ +| RELATIONSHIP CHAIN DETAILS | ++----------------------------------------------------------------------+ +| Entities: X total (DB:X API:X Comp:X Page:X) | +| Relations: X total | +| DB → API: X aligned, Y mismatches | +| API → Component: X aligned, Y mismatches | +| Component → Page: X aligned, Y mismatches | +| Circular deps: X detected | +| Layer violations: X detected | ++======================================================================+ +| TASK RESULTS | ++----------------------------------------------------------------------+ +| Total: X tasks | +| Approved: X tasks | +| Rejected: X tasks | +| Skipped: X tasks (already completed) | ++======================================================================+ +| APPROVED TASKS | +| - task_create_Button | +| - task_create_Form | ++----------------------------------------------------------------------+ +| REJECTED TASKS | +| - task_create_Modal | +| Reason: Missing file app/components/Modal.tsx | +| - task_update_API | +| Reason: API contract error - endpoint not found | ++======================================================================+ +| SECURITY WARNINGS | +| - src/lib/api.ts:15 - Possible hardcoded API key | +| - app/page.tsx:42 - dangerouslySetInnerHTML usage | ++======================================================================+ +``` + +### Step A7: Next Steps [MANDATORY] + +**IF all approved**: +``` +All tasks approved. +Next: Run `/workflow:approve implementation` to continue. +``` + +**IF any rejected**: +``` +Some tasks need fixes. + +API Contract Issues: + For frontend issues: Fix the API call URL or method + For backend issues: Create or fix the API endpoint + +For each rejected task, run: + /workflow:frontend (for frontend tasks) + /workflow:backend (for backend tasks) + +Then re-run: /workflow:review --auto +``` + +--- + +## MODE: SINGLE TASK REVIEW (--next or task_id) + +### Step S1: Get Task [MANDATORY] +```bash +VERSION_ID=$(python3 skills/guardrail-orchestrator/scripts/version_manager.py current) +TASKS_DIR=".workflow/versions/$VERSION_ID/tasks" +``` + +**IF --next**: +```bash +# Find first task with status: pending or status: implemented +TASK_FILE=$(grep -l "status: pending\|status: implemented" $TASKS_DIR/*.yml 2>/dev/null | head -1) +``` + +**IF specific task_id**: +```bash +TASK_FILE="$TASKS_DIR/$ARGUMENTS.yml" +``` + +**BLOCK IF**: Task file does not exist -> Error: "Task not found: $ARGUMENTS" + +### Step S2: Read Task Spec [MANDATORY] +```bash +cat "$TASK_FILE" +``` + +Extract: +- `id`: Task identifier +- `file_paths`: List of files to verify +- `acceptance_criteria`: List of requirements to check + +### Step S3: Run All Validations [MANDATORY] + +```bash +# Build +npm run build 2>&1 +BUILD_EXIT=$? + +# TypeScript +npx tsc --noEmit 2>&1 +TS_EXIT=$? + +# API Contract +python3 skills/guardrail-orchestrator/scripts/validate_api_contract.py --project-dir . 2>&1 +API_EXIT=$? +``` + +**MUST capture and report all exit codes** + +### Step S4: Verify Files Exist [MANDATORY] + +For each path in `file_paths`: +```bash +ls -la "$path" 2>/dev/null && echo "EXISTS" || echo "MISSING" +``` + +**Record**: +- FILES_EXIST = true/false +- MISSING_FILES = list of missing paths + +### Step S5: Read and Analyze Files [MANDATORY] + +For each EXISTING file: +1. Read file content +2. Check against acceptance_criteria: + - [ ] File exports correct components/functions + - [ ] Props/types match manifest spec + - [ ] Code follows project patterns + - [ ] No obvious bugs or issues +3. Check API contract compliance: + - [ ] Frontend calls use correct endpoints + - [ ] HTTP methods are appropriate + - [ ] Request bodies are properly structured + - [ ] Response handling is correct + +### Step S6: Determine Verdict [MANDATORY] + +``` +IF BUILD_EXIT = 0 + AND TS_EXIT = 0 + AND API_EXIT = 0 + AND FILES_EXIST = true + AND acceptance_criteria met: + -> VERDICT = APPROVED +ELSE: + -> VERDICT = REQUEST_CHANGES + -> Record all issues found +``` + +### Step S7: Update Task Status [MANDATORY] + +**IF APPROVED**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task $TASK_ID approved +``` + +**IF REQUEST_CHANGES**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py task $TASK_ID pending +``` + +### Step S8: Output Review Report [MANDATORY] + +**MUST output this exact format**: +``` ++======================================================================+ +| TASK REVIEW | ++======================================================================+ +| Task: $TASK_ID | +| Version: $VERSION_ID | ++======================================================================+ +| VALIDATION CHECKS | ++----------------------------------------------------------------------+ +| Build: PASS / FAIL | +| TypeScript: PASS / FAIL | +| API Contract: PASS / FAIL | +| Files exist: PASS / FAIL (X/Y files) | +| Acceptance criteria: PASS / PARTIAL / FAIL | +| Code quality: PASS / ISSUES | ++======================================================================+ +| API CONTRACT STATUS | ++----------------------------------------------------------------------+ +| Endpoint calls: X found, Y matched | +| Method correctness: PASS / X mismatches | +| Request bodies: PASS / X issues | +| Response handling: PASS / ISSUES | ++======================================================================+ +| VERDICT: APPROVED / REQUEST_CHANGES | ++======================================================================+ +| [If REQUEST_CHANGES, list all issues:] | +| 1. Missing file: app/components/Button.tsx | +| 2. TypeScript error: Type 'string' not assignable to 'number' | +| 3. API contract: POST /api/users called but endpoint expects GET | +| 4. API contract: Frontend sends body but backend ignores it | +| 5. Acceptance criterion not met: "Must support dark mode" | ++======================================================================+ +``` + +### Step S9: Next Steps [MANDATORY] + +**IF APPROVED**: +``` +Task approved. +Next: Run `/workflow:complete $TASK_ID` to mark as done. + Or run `/workflow:review --next` to review next task. +``` + +**IF REQUEST_CHANGES**: +``` +Changes requested. + +Issues to fix: + [List specific issues with file locations] + +For API contract issues: + - If frontend issue: Fix the fetch/axios call in [file:line] + - If backend issue: Update the API route handler in [file] + +Fix issues and re-run: + /workflow:frontend $TASK_ID (for frontend tasks) + /workflow:backend $TASK_ID (for backend tasks) +Then: /workflow:review $TASK_ID +``` + +--- + +## USAGE EXAMPLES + +```bash +# Review specific task +/workflow:review task_create_Button + +# Review next pending task +/workflow:review --next + +# Auto-review all tasks (standard - build + types + API) +/workflow:review --auto + +# Auto-review all tasks (strict - includes lint + tests) +/workflow:review --auto --strict + +# Full review with all checks +/workflow:review --auto --full +``` + +--- + +## API CONTRACT VALIDATION DETAILS + +The API contract validator checks: + +### Frontend Analysis +- **fetch()** calls with `/api/` paths +- **axios** requests (get, post, put, delete) +- **useSWR** data fetching hooks +- **Custom API clients** (api.get, api.post, etc.) + +### Backend Analysis +- **Next.js App Router**: `app/api/*/route.ts` exports (GET, POST, PUT, DELETE) +- **Next.js Pages Router**: `pages/api/*.ts` with req.method checks +- **Express-style**: router.get/post/etc patterns + +### Validation Rules +1. **Endpoint Existence**: Every frontend call must have a matching backend route +2. **Method Match**: GET calls must hit GET endpoints, POST to POST, etc. +3. **Body Alignment**: POST/PUT calls should send bodies, GET should not +4. **Unused Endpoints**: Backend routes not called by frontend (warnings) + +--- + +## ENFORCEMENT CHECKLIST + +Before completing this command, verify: +- [ ] Build command executed and exit code captured +- [ ] TypeScript check executed and exit code captured +- [ ] Async/await validation executed and exit code captured +- [ ] API contract validation executed and exit code captured +- [ ] **Relationship chain validation executed and exit code captured** +- [ ] All file_paths verified with ls command +- [ ] Security scan completed +- [ ] Structured review report output (exact format above) +- [ ] Task status updated via workflow_manager.py +- [ ] Next steps clearly stated diff --git a/.claude/commands/workflow/security.md b/.claude/commands/workflow/security.md new file mode 100644 index 0000000..d76fd24 --- /dev/null +++ b/.claude/commands/workflow/security.md @@ -0,0 +1,342 @@ +--- +description: Run comprehensive security audit (Security Reviewer agent) +allowed-tools: Read, Bash, Grep, Task +--- + +# Security Reviewer Agent - Security Audit Mode + +**Input**: "$ARGUMENTS" + +--- + +## CRITICAL CONSTRAINTS + +**YOU ARE IN READ-ONLY MODE FOR ANALYSIS.** + +### MUST DO (Non-Negotiable) +1. **MUST** run automated security scanner +2. **MUST** analyze all CRITICAL and HIGH findings +3. **MUST** check dependency vulnerabilities +4. **MUST** review security configurations +5. **MUST** output structured security report +6. **MUST** provide remediation guidance + +### CANNOT DO (Strictly Forbidden) +1. **CANNOT** modify source files +2. **CANNOT** fix issues directly +3. **CANNOT** approve with CRITICAL issues +4. **CANNOT** skip any security category + +--- + +## ARGUMENT PARSING + +``` +IF "$ARGUMENTS" contains "--quick": + MODE = QUICK (scanner only) +ELSE IF "$ARGUMENTS" contains "--full": + MODE = FULL (scanner + deep analysis + deps + config) +ELSE: + MODE = STANDARD (scanner + deps) + +SEVERITY = extract from --severity [critical|high|medium|low] +OUTPUT = extract from --json (JSON output) or text +``` + +--- + +## EXECUTION FLOW + +### Step 1: Run Automated Security Scanner [MANDATORY] + +```bash +python3 skills/guardrail-orchestrator/scripts/security_scan.py \ + --project-dir . \ + --severity ${SEVERITY:-LOW} \ + ${OUTPUT:+--json} +``` + +**Capture output and exit code:** +```bash +SCAN_EXIT=$? +echo "SCAN_EXIT=$SCAN_EXIT" +``` + +**Exit codes:** +- 0 = PASS (no critical/high issues) +- 1 = HIGH issues found +- 2 = CRITICAL issues found + +### Step 2: Dependency Audit [MANDATORY unless --quick] + +```bash +echo "=== Dependency Audit ===" +npm audit --json 2>/dev/null || echo '{"vulnerabilities":{}}' +``` + +**Parse npm audit results:** +- Count critical, high, moderate, low vulnerabilities +- List affected packages and versions +- Note if fixes available (`npm audit fix`) + +### Step 3: Deep Analysis [FULL mode only] + +For each CRITICAL/HIGH finding from scanner: + +#### 3.1 Data Flow Tracing +Use Task agent with security-engineer subagent: +``` +Analyze data flow for vulnerability at [file:line]. +Trace user input from source to sink. +Identify all potential attack vectors. +Assess exploitability and impact. +``` + +#### 3.2 Attack Vector Analysis +For each vulnerability type: +- SQL Injection → Check if input reaches query without sanitization +- XSS → Check if input reaches DOM without encoding +- Command Injection → Check if input reaches shell without escaping +- Path Traversal → Check if input reaches file system without validation + +### Step 4: Configuration Review [FULL mode only] + +#### 4.1 CORS Configuration +```bash +grep -rn "cors\|Access-Control" app/ src/ pages/ --include="*.ts" --include="*.tsx" --include="*.js" +``` + +Check for: +- Wildcard origins (`*`) +- Credentials with permissive origins +- Missing CORS on sensitive endpoints + +#### 4.2 Security Headers +```bash +grep -rn "helmet\|Content-Security-Policy\|X-Frame-Options\|X-XSS-Protection" . --include="*.ts" --include="*.js" +``` + +Check for: +- Helmet middleware usage +- CSP configuration +- X-Frame-Options +- X-Content-Type-Options + +#### 4.3 Authentication Configuration +```bash +grep -rn "jwt\|session\|auth\|cookie" app/ src/ pages/ --include="*.ts" --include="*.tsx" +``` + +Check for: +- JWT algorithm (avoid 'none', prefer RS256) +- Session configuration +- Cookie flags (httpOnly, secure, sameSite) + +#### 4.4 Environment Variables +```bash +# Check .env files are gitignored +cat .gitignore 2>/dev/null | grep -E "\.env" + +# Check for env var usage +grep -rn "process\.env\." app/ src/ --include="*.ts" --include="*.tsx" | head -20 +``` + +### Step 5: Manual Review Checklist [FULL mode only] + +Read each file modified in current workflow and verify: + +**Input Validation** +- [ ] All user inputs validated +- [ ] Type checking enforced +- [ ] Length limits applied +- [ ] Format validation (email, URL, etc.) + +**Output Encoding** +- [ ] HTML encoding for DOM insertion +- [ ] URL encoding for URLs +- [ ] JSON encoding for API responses + +**Database Security** +- [ ] Parameterized queries used +- [ ] No string concatenation in queries +- [ ] Proper ORM usage + +**Authentication/Authorization** +- [ ] Auth checks on protected routes +- [ ] Role-based access control +- [ ] Session validation + +**Error Handling** +- [ ] Generic error messages to users +- [ ] No stack traces in production +- [ ] No sensitive data in logs + +### Step 6: Generate Security Report [MANDATORY] + +**MUST output this exact format:** + +``` ++======================================================================+ +| SECURITY AUDIT REPORT | ++======================================================================+ +| Mode: QUICK / STANDARD / FULL | +| Date: [current date] | +| Project: [project name from package.json] | ++======================================================================+ +| RISK ASSESSMENT | ++----------------------------------------------------------------------+ +| Overall Risk: CRITICAL / HIGH / MEDIUM / LOW / PASS | +| | +| Static Analysis: X issues (C:X H:X M:X L:X) | +| Dependencies: X vulnerabilities | +| Configuration: X concerns | ++======================================================================+ +| CRITICAL ISSUES (Immediate Action Required) | ++----------------------------------------------------------------------+ +| [1] [CATEGORY] Title | +| Location: file:line | +| CWE: CWE-XXX | +| OWASP: A0X:2021-Category | +| Evidence: [code snippet] | +| Impact: [description of potential attack] | +| Fix: [specific remediation steps] | +| | +| [2] ... | ++======================================================================+ +| HIGH ISSUES (Fix Before Production) | ++----------------------------------------------------------------------+ +| [3] ... | ++======================================================================+ +| MEDIUM ISSUES (Should Fix) | ++----------------------------------------------------------------------+ +| [4] ... | ++======================================================================+ +| DEPENDENCY VULNERABILITIES | ++----------------------------------------------------------------------+ +| Package Version Severity Fix Available | +| lodash 4.17.20 HIGH npm audit fix | +| axios 0.21.0 MEDIUM npm audit fix | ++======================================================================+ +| CONFIGURATION CONCERNS | ++----------------------------------------------------------------------+ +| - CORS: Wildcard origin detected in src/middleware.ts | +| - Session: Missing httpOnly flag on auth cookie | +| - Headers: No CSP header configured | ++======================================================================+ +| REMEDIATION PRIORITY | ++----------------------------------------------------------------------+ +| 1. [CRITICAL] Rotate exposed API key in src/lib/api.ts | +| 2. [CRITICAL] Fix SQL injection in app/api/users/route.ts | +| 3. [HIGH] Update lodash to 4.17.21 | +| 4. [HIGH] Add input validation to user registration | +| 5. [MEDIUM] Configure CSP headers | ++======================================================================+ +| VERDICT | ++----------------------------------------------------------------------+ +| FAIL - X critical issues must be fixed before deployment | +| or | +| PASS - No blocking security issues found | ++======================================================================+ +``` + +--- + +## VERDICT DETERMINATION + +### FAIL Conditions +- Any CRITICAL issue found +- 3+ HIGH issues found +- Critical npm vulnerabilities without fix +- Exposed secrets or credentials + +### PASS WITH WARNINGS +- Only MEDIUM/LOW issues +- All HIGH issues have accepted risk +- Dependencies have fixes available + +### PASS +- No CRITICAL/HIGH issues +- Dependencies up to date +- Configurations reviewed + +--- + +## POST-AUDIT ACTIONS + +### If FAIL: +``` +SECURITY AUDIT FAILED + +Blocking issues must be fixed: +1. [List critical issues] + +For each issue: + /workflow:frontend - if frontend issue + /workflow:backend - if backend issue + +Then re-run: /workflow:security +``` + +### If PASS: +``` +SECURITY AUDIT PASSED + +Proceed with: /workflow:review --auto +``` + +--- + +## USAGE EXAMPLES + +```bash +# Quick scan (automated scanner only) +/workflow:security --quick + +# Standard scan (scanner + dependencies) +/workflow:security + +# Full audit (all checks) +/workflow:security --full + +# Filter by severity +/workflow:security --severity high + +# JSON output for CI/CD +/workflow:security --json +``` + +--- + +## INTEGRATION WITH CI/CD + +### Pre-commit Hook +```bash +python3 skills/guardrail-orchestrator/scripts/security_scan.py \ + --project-dir . --severity HIGH --strict +``` + +### GitHub Actions +```yaml +- name: Security Scan + run: | + python3 skills/guardrail-orchestrator/scripts/security_scan.py \ + --project-dir . --json > security-report.json + + if [ $? -ne 0 ]; then + echo "Security issues found!" + cat security-report.json + exit 1 + fi +``` + +--- + +## ENFORCEMENT CHECKLIST + +Before completing this command, verify: +- [ ] Automated scanner executed +- [ ] All categories analyzed +- [ ] Dependencies audited (unless --quick) +- [ ] Structured report output +- [ ] Remediation guidance provided +- [ ] Clear verdict stated diff --git a/.claude/commands/workflow/spawn.md b/.claude/commands/workflow/spawn.md new file mode 100644 index 0000000..053bfbb --- /dev/null +++ b/.claude/commands/workflow/spawn.md @@ -0,0 +1,2274 @@ +--- +description: Automated workflow orchestrator with approval gates and sub-agent delegation +allowed-tools: Read, Write, Edit, Bash, Task, AskUserQuestion, TodoWrite +agents: workflow-orchestrator, workflow-reviewer, workflow-validator, type-generator, backend-implementer, frontend-implementer, deployer +--- + +# Workflow Orchestrator - Spawn + +**Input**: "$ARGUMENTS" + +--- + +## ⛔ CRITICAL ENFORCEMENT RULES + +**READ THESE RULES BEFORE ANY ACTION. VIOLATIONS WILL CORRUPT THE WORKFLOW.** + +### 🔴 MUST DO (Non-Negotiable) +1. **MUST** run version_manager.py create BEFORE any design work +2. **MUST** verify phase state BEFORE each transition +3. **MUST** run validation script AFTER each phase completes +4. **MUST** capture and display script output (never assume success) +5. **MUST** create task files in `.workflow/versions/vXXX/tasks/` (version-specific) +6. **MUST** wait for Task agents to fully complete before proceeding +7. **MUST** run `npm run build` and verify exit code = 0 before approval + +### 🚫 CANNOT DO (Strictly Forbidden) +1. **CANNOT** skip phases or combine phases +2. **CANNOT** proceed if any verification fails +3. **CANNOT** assume task files exist - must verify with `ls` +4. **CANNOT** assume build passes - must run and check exit code +5. **CANNOT** transition without running the transition script +6. **CANNOT** mark workflow complete if any task is not 'approved' +7. **CANNOT** proceed to IMPLEMENTING if no task files exist + +### ⚠️ BLOCKING CONDITIONS +These conditions **HALT** the workflow immediately: + +| Condition | Blocked Phase | Resolution | +|-----------|---------------|------------| +| `project_manifest.json` missing | INITIALIZE | Create manifest first | +| No task files created | DESIGNING → IMPLEMENTING | Architect must create tasks | +| Build fails | IMPLEMENTING → REVIEWING | Fix build errors | +| Files missing | REVIEWING | Implement missing files | +| Version mismatch | Any | Run `/workflow:status` | + +--- + +## CUSTOM AGENTS + +**Use these project-specific agents at the indicated phases:** + +| Agent | When to Use | Phase | Parallel | +|-------|-------------|-------|----------| +| `workflow-orchestrator` | Overall workflow coordination and delegation | All phases | - | +| `workflow-reviewer` | Gap analysis after design changes | DESIGN_REVIEW | - | +| `type-generator` | Generate TypeScript types from design | Pre-IMPLEMENTING | - | +| `backend-implementer` | Prisma models, API routes, server logic | IMPLEMENTING | ✅ Yes | +| `frontend-implementer` | React components, pages, client logic | IMPLEMENTING | ✅ Yes | +| `workflow-validator` | Validate implementation against design | During/after IMPLEMENTING | - | +| `deployer` | Deploy to Eureka platform | After APPROVAL / COMPLETED | - | + +### Parallel Execution + +**Backend + Frontend run in parallel:** +``` +Use Task tool with: Use Task tool with: + subagent_type: "general-purpose" subagent_type: "general-purpose" + prompt: | prompt: | + You are backend-implementer. You are frontend-implementer. + [Read .claude/agents/backend- [Read .claude/agents/frontend- + implementer.md] implementer.md] + Task: Implement backend tasks... Task: Implement frontend tasks... +``` + +**CRITICAL: Send BOTH Task calls in a SINGLE message for parallel execution.** + +### Agent Invocation + +**To use custom agents, invoke via Task tool:** +``` +Use Task tool with: + subagent_type: "general-purpose" + prompt: | + You are the workflow-validator agent. + [Read .claude/agents/workflow-validator.md for full instructions] + + Task: Validate implementation against design document... +``` + +**Or reference agent directly:** +``` +Use the workflow-validator agent to check implementation compliance. +``` + +--- + +## ARGUMENT PARSING + +``` +FULL_AUTO_MODE = "$ARGUMENTS" contains "--full-auto" +AUTO_MODE = "$ARGUMENTS" contains "--auto" AND NOT "--full-auto" +MANUAL_MODE = NOT AUTO_MODE AND NOT FULL_AUTO_MODE +FEATURE = "$ARGUMENTS" with "--auto" and "--full-auto" removed and trimmed +``` + +### Mode Comparison + +| Aspect | Manual | --auto | --full-auto | +|--------|--------|--------|-------------| +| Requirements | User provides all | AI asks questions with options | AI expands autonomously | +| Questions | None | Until AI has enough info | Only acceptance criteria | +| Design Approval | Manual | Auto-approve | Auto-approve | +| Impl Approval | Manual | Auto if validation passes | Auto if validation passes | +| Best For | Full control | Guided discovery | Quick prototyping | + +### --auto Behavior (Interactive Discovery) +- **PHASE 1.5**: AI asks clarifying questions with multiple-choice options +- Questions continue until AI determines requirements are complete +- Examples: "What auth method?", "Need password reset?", "Which OAuth providers?" +- Gate 1 (design approval): Auto-approve +- Gate 2 (impl approval): Auto-approve IF validation passes +- **STILL RUNS**: All validation checks, build verification + +### --full-auto Behavior (AI-Driven Expansion) +- **PHASE 1.5**: AI autonomously analyzes and expands the idea +- AI generates comprehensive requirements from brief input +- **ONLY ASKS**: Acceptance criteria ("How will you know this is done?") +- Gate 1 (design approval): Auto-approve +- Gate 2 (impl approval): Auto-approve IF validation passes +- **STILL RUNS**: All validation checks, build verification + +--- + +## PHASE EXECUTION PROTOCOL + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1: INITIALIZE +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Command invoked +**Exit Condition**: Version created, session started, phase = DESIGNING + +#### Step 1.1: Parse Arguments [MANDATORY] +``` +Extract: AUTO_MODE, FEATURE +Validate: FEATURE is not empty +``` +**BLOCK IF**: FEATURE is empty → Ask user for feature description + +#### Step 1.2: Verify Prerequisites [MANDATORY] +```bash +# MUST run this check - do not skip +ls project_manifest.json +``` +**BLOCK IF**: File does not exist → Error: "Run /guardrail:init or /guardrail:analyze first" + +```bash +# Create workflow directory if missing (auto-recovery) +mkdir -p .workflow/versions +if [ ! -f .workflow/index.yml ]; then + cat > .workflow/index.yml << 'EOF' +versions: [] +latest_version: null +total_versions: 0 +EOF +fi +``` + +#### Step 1.3: Create Version [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/version_manager.py create "$FEATURE" +``` +**MUST capture output** and extract VERSION_ID (e.g., "v004") +**BLOCK IF**: Script fails → Error with script output + +#### Step 1.4: Display Start Banner [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🚀 WORKFLOW STARTED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Version: $VERSION_ID ║ +║ Feature: $FEATURE ║ +║ Mode: $MODE (AUTO/INTERACTIVE) ║ +║ Tasks: .workflow/versions/$VERSION_ID/tasks/ ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 1.5: Transition [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition DESIGNING +``` +**VERIFY**: Script exits with code 0 + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1.5: REQUIREMENTS GATHERING (--auto and --full-auto only) +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = DESIGNING, Mode = AUTO_MODE or FULL_AUTO_MODE +**Exit Condition**: Requirements documented, ready for design +**SKIP IF**: MANUAL_MODE (proceed directly to PHASE 2) + +--- + +#### Step 1.5.0: Launch Background Context Gatherer [PARALLEL - ALL MODES] + +**CRITICAL**: Launch this agent IMMEDIATELY at the start of Phase 1.5. It runs in the BACKGROUND while you ask questions, gathering existing project context so the design phase has full information ready. + +``` +Use Task tool with: + subagent_type: "general-purpose" + run_in_background: true + prompt: | + You are the context-gatherer agent. + Read and follow: .claude/agents/context-gatherer.md + + ## YOUR MISSION + Gather all existing project context QUICKLY so the design phase can use it. + You are running in BACKGROUND while the main workflow asks user questions. + + ## GATHER THESE (in parallel where possible): + + ### 1. Project Manifest + ```bash + cat project_manifest.json 2>/dev/null | head -200 + ``` + + ### 2. Existing Design Documents + ```bash + find .workflow/versions -name "design_document.yml" 2>/dev/null | head -3 | xargs -I{} head -100 {} + ``` + + ### 3. Relations/Dependencies + ```bash + find .workflow/versions -name "relations.yml" 2>/dev/null | head -3 | xargs -I{} cat {} + ``` + + ### 4. Existing Codebase Structure + ```bash + # Prisma models + cat prisma/schema.prisma 2>/dev/null | grep -A 15 "^model " | head -80 + + # API routes + find app/api -name "route.ts" 2>/dev/null | head -15 + + # Pages + find app -name "page.tsx" -not -path "*/api/*" 2>/dev/null | head -15 + + # Components + find app/components components -name "*.tsx" 2>/dev/null | head -20 + + # Types + find types app/types -name "*.ts" 2>/dev/null | head -10 + ``` + + ### 5. Detect Patterns + ```bash + # Tech stack from package.json + cat package.json 2>/dev/null | grep -E '"(next|react|prisma|@prisma|tailwind)"' | head -10 + ``` + + ## OUTPUT + Write consolidated context to: .workflow/gathered_context.yml + + Include: + - existing_models: list with fields and relations + - existing_apis: list with methods and paths + - existing_pages: list with routes + - existing_components: list with props + - dependency_chains: what depends on what + - detected_patterns: naming, file structure, imports + - reference_implementations: good examples to follow + + ## SPEED REQUIREMENT + Complete within 15 seconds. Use head/grep to limit output. + DO NOT ask questions - you are background process. +``` + +**WHY PARALLEL**: While this agent gathers context, continue with questions below. +The gathered context will be ready when design phase starts. + +--- + +#### IF AUTO_MODE: Interactive Requirements Discovery + +**Purpose**: Ask clarifying questions until AI has enough information to design the system. + +##### Step 1.5.1: Initialize Requirements Document +```bash +mkdir -p .workflow/versions/$VERSION_ID/requirements +``` + +Create requirements tracking: +```yaml +# .workflow/versions/$VERSION_ID/requirements/discovery.yml +feature: "$FEATURE" +status: gathering +questions_asked: 0 +requirements: [] +ready_for_design: false +``` + +##### Step 1.5.2: Question Loop [MANDATORY] + +**REPEAT until ready_for_design = true:** + +Use AskUserQuestion tool to gather requirements: + +``` +Use AskUserQuestion with intelligent questions based on feature type. + +Question categories to explore: +1. SCOPE: What exactly should this feature do? +2. USERS: Who will use this? What roles? +3. DATA: What information needs to be stored? +4. ACTIONS: What operations can users perform? +5. AUTH: What security/permissions are needed? +6. UI: What screens/components are needed? +7. INTEGRATIONS: Any external services? +8. EDGE CASES: What happens when X fails? +``` + +**Example Question Flow for "add user authentication":** + +``` +Round 1 - Authentication Type: + Question: "What authentication method do you need?" + Options: + - "Email/Password (Recommended)" - Traditional login with email and password + - "OAuth Social Login" - Login with Google, GitHub, etc. + - "Magic Link" - Passwordless email link login + - "Multi-factor" - 2FA with authenticator apps + [multiSelect: true] + +Round 2 - (If OAuth selected) Providers: + Question: "Which OAuth providers should be supported?" + Options: + - "Google (Recommended)" - Most common, easy setup + - "GitHub" - Popular for developer tools + - "Apple" - Required for iOS apps + - "Microsoft" - Common for enterprise + [multiSelect: true] + +Round 3 - User Data: + Question: "What user information should be stored?" + Options: + - "Basic (name, email)" - Minimal user profile + - "Extended (+ avatar, bio)" - Social features + - "Professional (+ company, role)" - B2B applications + - "Custom fields" - I'll specify additional fields + [multiSelect: false] + +Round 4 - Features: + Question: "Which additional features do you need?" + Options: + - "Password reset" - Email-based password recovery + - "Email verification" - Confirm email ownership + - "Remember me" - Persistent sessions + - "Account deletion" - GDPR compliance + [multiSelect: true] + +Round 5 - UI Components: + Question: "What UI components are needed?" + Options: + - "Login page" - Standalone login screen + - "Registration page" - New user signup + - "Profile page" - View/edit user info + - "Settings page" - Account settings + [multiSelect: true] +``` + +##### Step 1.5.3: Evaluate Completeness [MANDATORY] + +After each round, evaluate if requirements are sufficient: + +``` +READY_FOR_DESIGN = true IF ALL of these are answered: + - [ ] Core functionality is clear + - [ ] Data model requirements are known + - [ ] API operations are identified + - [ ] UI screens are listed + - [ ] Authentication/authorization is defined + - [ ] Key edge cases are addressed +``` + +**If NOT ready**: Generate next question based on gaps +**If ready**: Proceed to Step 1.5.4 + +##### Step 1.5.4: Generate Requirements Summary [MANDATORY] + +Save gathered requirements: +```yaml +# .workflow/versions/$VERSION_ID/requirements/summary.yml +feature: "$FEATURE" +gathered_at: +questions_asked: X +mode: auto + +requirements: + authentication: + methods: [email_password, oauth_google] + features: [password_reset, email_verification] + + data_model: + user_fields: [name, email, avatar, bio] + additional_entities: [] + + ui_components: + pages: [login, register, profile] + components: [login_form, user_avatar] + + api_endpoints: + - POST /api/auth/login + - POST /api/auth/register + - POST /api/auth/forgot-password + - GET /api/users/me + +acceptance_criteria: + - User can register with email/password + - User can login with Google OAuth + - User can reset forgotten password + - User profile displays correctly +``` + +##### Step 1.5.5: Display Requirements Summary [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📋 REQUIREMENTS GATHERED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Feature: $FEATURE ║ +║ Questions asked: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ SCOPE DEFINED ║ +║ ✅ Authentication: Email/Password + Google OAuth ║ +║ ✅ Features: Password reset, Email verification ║ +║ ✅ User Data: Name, email, avatar, bio ║ +║ ✅ UI: Login, Register, Profile pages ║ +╠══════════════════════════════════════════════════════════════╣ +║ Proceeding to DESIGN phase... ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +#### IF FULL_AUTO_MODE: AI-Driven Expansion + +**Purpose**: AI autonomously expands brief input into comprehensive requirements. +Only asks user for acceptance criteria. + +##### Step 1.5.1: Autonomous Analysis [MANDATORY] + +Use Task tool to expand requirements: + +``` +Use Task tool with: + subagent_type: "requirements-analyst" + prompt: | + # REQUIREMENTS ANALYST - Autonomous Expansion + + ## INPUT + Feature request: "$FEATURE" + + ## YOUR MISSION + Expand this brief feature request into comprehensive requirements. + Think like a senior product manager. + + ## ANALYSIS PROCESS + + 1. **Understand Intent** + - What problem is the user trying to solve? + - What is the core value proposition? + - Who are the target users? + + 2. **Expand Scope** + - What are the obvious features needed? + - What are commonly expected features users don't mention? + - What are the MVP requirements vs nice-to-haves? + + 3. **Data Requirements** + - What entities need to be stored? + - What are the relationships between entities? + - What fields does each entity need? + + 4. **API Design** + - What CRUD operations are needed? + - What custom operations are needed? + - What authentication/authorization is required? + + 5. **UI Components** + - What pages/screens are needed? + - What reusable components are needed? + - What is the user flow? + + 6. **Edge Cases** + - What happens on errors? + - What are the validation rules? + - What are the security considerations? + + ## OUTPUT FORMAT + + Create: .workflow/versions/$VERSION_ID/requirements/expanded.yml + + ```yaml + feature: "$FEATURE" + expanded_at: + mode: full_auto + + analysis: + problem_statement: "" + target_users: "" + core_value: "
" + + scope: + mvp_features: + - + - + future_features: + - + + data_model: + entities: + - name: + fields: [, ] + relations: [] + + api_endpoints: + - method: POST + path: /api/xxx + purpose: + + ui_structure: + pages: + - name: + route: / + purpose: + components: + - name: + purpose: + + security: + authentication: + authorization: + + edge_cases: + - scenario: + handling: + ``` + + ## OUTPUT + After creating the file, output a summary of your analysis. +``` + +##### Step 1.5.2: Display Expanded Requirements [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🤖 AI-EXPANDED REQUIREMENTS ║ +╠══════════════════════════════════════════════════════════════╣ +║ Original: "$FEATURE" ║ +╠══════════════════════════════════════════════════════════════╣ +║ EXPANDED SCOPE ║ +║ Problem: ║ +║ Users: ║ +║ Value: ║ +╠══════════════════════════════════════════════════════════════╣ +║ MVP FEATURES ║ +║ • Feature 1 ║ +║ • Feature 2 ║ +║ • Feature 3 ║ +╠══════════════════════════════════════════════════════════════╣ +║ DATA MODEL ║ +║ 📦 Entity1 (X fields) ║ +║ 📦 Entity2 (Y fields) ║ +╠══════════════════════════════════════════════════════════════╣ +║ API ENDPOINTS: X ║ +║ UI PAGES: X ║ +║ COMPONENTS: X ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +##### Step 1.5.3: Ask for Acceptance Criteria [MANDATORY] + +**This is the ONLY question in full-auto mode:** + +``` +Use AskUserQuestion: + Question: "Based on the expanded requirements above, what are your acceptance criteria? How will you know this feature is complete and working?" + + Options: + - "Looks good - use AI-suggested criteria" + Description: "AI will generate acceptance criteria based on the requirements" + - "I'll specify my own criteria" + Description: "Enter your own acceptance criteria" + - "Add to AI criteria" + Description: "Use AI criteria plus add my own" +``` + +**If user chooses "Looks good"**: AI generates acceptance criteria +**If user chooses "I'll specify"**: Prompt user for criteria +**If user chooses "Add to"**: Combine AI + user criteria + +##### Step 1.5.4: Finalize Requirements [MANDATORY] + +Save final requirements with acceptance criteria: +```yaml +# .workflow/versions/$VERSION_ID/requirements/final.yml +feature: "$FEATURE" +mode: full_auto +finalized_at: + +# ... expanded requirements ... + +acceptance_criteria: + - criterion: "User can successfully register" + verification: "Submit registration form, verify account created" + - criterion: "User can login with credentials" + verification: "Login with valid credentials, verify session created" + - criterion: "Invalid login shows error" + verification: "Login with wrong password, verify error message" + # ... more criteria +``` + +##### Step 1.5.5: Display Final Summary [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ REQUIREMENTS FINALIZED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Feature: $FEATURE ║ +║ Mode: Full-Auto ║ +╠══════════════════════════════════════════════════════════════╣ +║ ACCEPTANCE CRITERIA ║ +║ ☐ User can successfully register ║ +║ ☐ User can login with credentials ║ +║ ☐ Invalid login shows error ║ +║ ☐ Password reset works via email ║ +╠══════════════════════════════════════════════════════════════╣ +║ Proceeding to DESIGN phase (auto-approved)... ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +#### IF MANUAL_MODE: Skip to Design + +**No requirements gathering phase. Proceed directly to PHASE 2.** + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 2: DESIGNING (Enhanced with Design Document) +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = DESIGNING (verified) +**Exit Condition**: Design document validated, dependency graph generated, tasks with context created + +#### Step 2.1: Verify Phase State [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` +**BLOCK IF**: Current phase is not DESIGNING + +#### Step 2.2: Create Design Directories [MANDATORY] +```bash +mkdir -p .workflow/versions/$VERSION_ID/design +mkdir -p .workflow/versions/$VERSION_ID/contexts +mkdir -p .workflow/versions/$VERSION_ID/tasks +``` + +#### Step 2.2.5: Retrieve Background Context [MANDATORY] +**Wait for and retrieve the gathered context from the background agent:** + +```bash +# Check if gathered context exists (background agent completed) +if [ -f ".workflow/gathered_context.yml" ]; then + echo "✅ Background context gathered successfully" + cat .workflow/gathered_context.yml | head -100 +else + echo "⚠️ Background context not yet available, proceeding without" +fi +``` + +**Display gathered context summary:** +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📊 EXISTING PROJECT CONTEXT (Gathered in Background) ║ +╠══════════════════════════════════════════════════════════════╣ +║ Existing Models: X (list: model_user, model_post, ...) ║ +║ Existing APIs: X (list: api_get_users, ...) ║ +║ Existing Pages: X (list: page_home, ...) ║ +║ Existing Components: X (list: component_header, ...) ║ +╠══════════════════════════════════════════════════════════════╣ +║ DEPENDENCIES TO CONSIDER ║ +║ Most connected: model_user (used by X entities) ║ +║ Reusable: component_header, component_card, ... ║ +╠══════════════════════════════════════════════════════════════╣ +║ This context will be passed to the architect agent below. ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 2.3: Spawn Architect Agent for Design Document [MANDATORY] +**MUST use Task tool to create comprehensive design document**: + +``` +Use Task tool with: + subagent_type: "system-architect" + prompt: | + # SYSTEM ARCHITECT - Design Document Creation + # VERSION $VERSION_ID + + ## STRICT REQUIREMENTS + Create a COMPLETE design document. Partial designs = failure. + + ## INPUT + Feature: "$FEATURE" + Version: $VERSION_ID + Output: .workflow/versions/$VERSION_ID/design/design_document.yml + Schema: skills/guardrail-orchestrator/schemas/design_document.yml + + ## 🔴 CRITICAL: READ EXISTING CONTEXT FIRST + Before designing, READ the gathered context to understand what already exists: + ```bash + cat .workflow/gathered_context.yml 2>/dev/null + ``` + + This tells you: + - **Existing models** you can reference or extend (don't recreate!) + - **Existing APIs** you can reuse + - **Existing components** you can compose + - **Dependency chains** to understand impact + - **Patterns** to follow for consistency + + ## DESIGN PRINCIPLES WITH CONTEXT + + 1. **REUSE before CREATE**: If model_user exists, use it - don't create model_customer + 2. **EXTEND over DUPLICATE**: Add fields to existing models when appropriate + 3. **FOLLOW PATTERNS**: Match existing naming (camelCase, PascalCase, file paths) + 4. **DECLARE DEPENDENCIES**: Link new entities to existing ones explicitly + + ## DESIGN PROCESS + + ### Phase A: Analyze Requirements + Existing Context + 1. Read gathered_context.yml for existing entities + 2. Break down "$FEATURE" into user stories + 3. Identify what ALREADY EXISTS vs what's NEW + 4. Plan integrations with existing entities + + ### Phase B: Design Data Layer (LAYER 1) + Create data_models section with: + - id: model_ + - name: PascalCase entity name + - table_name: snake_case + - fields: [name, type, constraints] + - relations: [type, target, foreign_key, on_delete] + - timestamps: true + - validations: [field, rule, message] + + ### Phase C: Design API Layer (LAYER 2) + Create api_endpoints section with: + - id: api__ + - method: GET|POST|PUT|PATCH|DELETE + - path: /api/ + - request_body: (for POST/PUT/PATCH) + - responses: [{status, description, schema}] + - depends_on_models: [model_ids] + - auth: {required, roles} + + ### Phase D: Design UI Layer (LAYER 3) + Create pages section with: + - id: page_ + - path: / + - data_needs: [{api_id, purpose, on_load}] + - components: [component_ids] + - auth: {required, roles, redirect} + + Create components section with: + - id: component_ + - name: PascalCaseName + - props: [{name, type, required, description}] + - events: [{name, payload, description}] + - uses_apis: [api_ids] + - uses_components: [component_ids] + + ## OUTPUT FORMAT + Create file: .workflow/versions/$VERSION_ID/design/design_document.yml + + ```yaml + workflow_version: "$VERSION_ID" + feature: "$FEATURE" + created_at: + status: draft + revision: 1 + + data_models: + - id: model_ + name: + # ... full model definition + + api_endpoints: + - id: api__ + method: + path: /api/ + # ... full endpoint definition + + pages: + - id: page_ + path: / + # ... full page definition + + components: + - id: component_ + name: + # ... full component definition + ``` + + ## ALSO UPDATE project_manifest.json + Add entities under appropriate sections with status: "PENDING" + + ## VERIFICATION + Before finishing, verify the design document exists: + ```bash + cat .workflow/versions/$VERSION_ID/design/design_document.yml | head -20 + ``` + + ## OUTPUT SUMMARY + ``` + === DESIGN DOCUMENT CREATED === + Data Models: X + API Endpoints: X + Pages: X + Components: X + File: .workflow/versions/$VERSION_ID/design/design_document.yml + ``` +``` + +#### Step 2.4: Validate Design & Generate Artifacts [MANDATORY] +**Run design validation to generate dependency graph, contexts, and tasks**: + +```bash +python3 skills/guardrail-orchestrator/scripts/validate_design.py \ + .workflow/versions/$VERSION_ID/design/design_document.yml \ + --output-dir .workflow/versions/$VERSION_ID +``` + +**This generates:** +- `dependency_graph.yml` - Layered execution order +- `contexts/*.yml` - Per-entity context snapshots for subagents +- `tasks/*.yml` - Implementation tasks with full context + +**BLOCK IF**: Validation fails (exit code != 0) → Display errors, re-run design + +#### Step 2.4.5: Generate API Contract & Shared Types [MANDATORY] +**Generate the API contract that binds frontend and backend implementations**: + +```bash +python3 skills/guardrail-orchestrator/scripts/generate_api_contract.py \ + .workflow/versions/$VERSION_ID/design/design_document.yml \ + --output-dir .workflow/versions/$VERSION_ID +``` + +**This generates:** +- `contracts/api_contract.yml` - Strict API type definitions +- `app/types/api.ts` - Shared TypeScript interfaces + +**CRITICAL**: Both frontend and backend agents MUST import from `app/types/api.ts`. +This ensures type safety and contract compliance. + +**BLOCK IF**: Generation fails → Display errors, re-run design + +#### Step 2.4.6: Build Entity Relationship Graph [MANDATORY] +**Build the relationship graph for impact analysis and validation**: + +```bash +python3 skills/guardrail-orchestrator/scripts/build_relations.py \ + --design-doc .workflow/versions/$VERSION_ID/design/design_document.yml \ + --project-dir . \ + --output .workflow/versions/$VERSION_ID/relations.yml +``` + +**This generates:** +- `relations.yml` - Entity relationship graph with: + - Database → API → Component → Page dependency chains + - Impact chains (what breaks if X changes) + - Type mappings for validation + - Circular dependency detection + +**Used for:** +- Change impact analysis during updates +- Type alignment validation during review +- Relationship integrity checks + +**BLOCK IF**: Generation fails or circular dependencies detected + +#### Step 2.5: Verify Generated Artifacts [MANDATORY] +```bash +# Check dependency graph exists +ls .workflow/versions/$VERSION_ID/dependency_graph.yml + +# Check API contract exists +ls .workflow/versions/$VERSION_ID/contracts/api_contract.yml + +# Check shared types file exists +ls app/types/api.ts + +# Count generated tasks +TASK_COUNT=$(ls .workflow/versions/$VERSION_ID/tasks/*.yml 2>/dev/null | wc -l) +echo "Tasks generated: $TASK_COUNT" + +# Count context files +CONTEXT_COUNT=$(ls .workflow/versions/$VERSION_ID/contexts/*.yml 2>/dev/null | wc -l) +echo "Context files: $CONTEXT_COUNT" +``` + +**BLOCK IF**: TASK_COUNT = 0 → Error: "No tasks generated from design" +**BLOCK IF**: API contract missing → Error: "API contract not generated" + +#### Step 2.6: Display Layered Execution Plan [MANDATORY] +Read dependency_graph.yml and display: + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📊 EXECUTION LAYERS (Dependency Graph) ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ Layer 1: DATA MODELS (Parallel) ║ +║ ───────────────────────────────────────────── ║ +║ 📦 model_xxx → backend [no deps] ║ +║ 📦 model_yyy → backend [no deps] ║ +║ ║ +║ Layer 2: API ENDPOINTS (After Layer 1) ║ +║ ───────────────────────────────────────────── ║ +║ 🔌 api_xxx → backend [needs: model_xxx] ║ +║ 🔌 api_yyy → backend [needs: model_xxx, model_yyy] ║ +║ ║ +║ Layer 3: UI (After Layer 2) ║ +║ ───────────────────────────────────────────── ║ +║ 🧩 component_xxx → frontend [no deps] ║ +║ 📄 page_xxx → frontend [needs: api_xxx, component_xxx]║ +║ ║ +╠══════════════════════════════════════════════════════════════╣ +║ EXECUTION SUMMARY ║ +║ Total tasks: X ║ +║ Total layers: X ║ +║ Max parallelism: X tasks can run simultaneously ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 2.7: Transition [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py progress \ + --tasks-created $TASK_COUNT +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition AWAITING_DESIGN_APPROVAL +``` + +#### Step 2.8: Display Design Summary [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📐 DESIGN COMPLETE - AWAITING APPROVAL ║ +╠══════════════════════════════════════════════════════════════╣ +║ Feature: $FEATURE ║ +║ Version: $VERSION_ID ║ +╠══════════════════════════════════════════════════════════════╣ +║ DESIGN DOCUMENT ║ +║ 📦 Data Models: X ║ +║ 🔌 API Endpoints: X ║ +║ 📄 Pages: X ║ +║ 🧩 Components: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ GENERATED ARTIFACTS ║ +║ ✅ Design document created ║ +║ ✅ Dependency graph calculated ║ +║ ✅ Context snapshots: X files ║ +║ ✅ Implementation tasks: X tasks ║ +╠══════════════════════════════════════════════════════════════╣ +║ Each subagent will receive FULL CONTEXT including: ║ +║ - Target entity definition ║ +║ - Related model/API definitions ║ +║ - Input/output contracts ║ +║ - Acceptance criteria ║ +╠══════════════════════════════════════════════════════════════╣ +║ 👆 Review the execution layers above ║ +║ ║ +║ If design looks correct: /workflow:approve ║ +║ If changes needed: /workflow:reject "reason" ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 3: GATE 1 - Design Approval +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = AWAITING_DESIGN_APPROVAL +**Exit Condition**: Design approved, types generated, phase = IMPLEMENTING + +#### IF AUTO_MODE = true: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py approve design +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition DESIGN_APPROVED +``` +Output: "✅ Design auto-approved. Generating types..." + +#### IF AUTO_MODE = false: +Use AskUserQuestion: +``` +Question: "Review the design. How do you want to proceed?" +Options: + 1. "Approve - Continue to implementation" + 2. "Reject - Revise design" + 3. "Pause - Save and exit" +``` + +**On Approve**: Run transition commands above +**On Reject**: Return to Phase 2 +**On Pause**: Output resume command and stop + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 3.5: TYPE GENERATION (MANDATORY) +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = DESIGN_APPROVED +**Exit Condition**: TypeScript types generated, phase = IMPLEMENTING + +**CRITICAL**: This phase generates shared types that ALL agents MUST use. +Skipping this phase will result in type mismatches between frontend and backend. + +#### Step 3.5.1: Generate TypeScript Types [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/generate_types.py \ + .workflow/versions/$VERSION_ID/design/design_document.yml \ + --output-dir types +``` + +**This generates:** +- `types/types.ts` - All data model interfaces +- `types/component-props.ts` - Component prop interfaces with typed objects +- `types/api-types.ts` - Request/response types for API endpoints +- `types/index.ts` - Re-exports all types + +#### Step 3.5.2: Verify Types Generated [MANDATORY] +```bash +# Check all type files exist +ls types/types.ts types/component-props.ts types/api-types.ts types/index.ts + +# Show type summary +echo "=== Generated Types ===" +grep "^export interface" types/types.ts | wc -l +grep "^export interface" types/component-props.ts | wc -l +grep "^export interface" types/api-types.ts | wc -l +``` +**BLOCK IF**: Any type file missing → Error: "Type generation failed" + +#### Step 3.5.3: Display Type Generation Summary [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📋 TYPES GENERATED ║ +╠══════════════════════════════════════════════════════════════╣ +║ types/types.ts - X model interfaces ║ +║ types/component-props.ts - X component prop interfaces ║ +║ types/api-types.ts - X API request/response types ║ +╠══════════════════════════════════════════════════════════════╣ +║ ⚠️ ALL AGENTS MUST: ║ +║ 1. Import types from @/types ║ +║ 2. Use typed object props (not flat props) ║ +║ 3. Implement ALL events from design ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 3.5.4: Generate Implementation Context Summary [MANDATORY] +**Create a consolidated context file that agents MUST read before EVERY task:** + +```bash +# Create implementation context summary +cat > .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md << CONTEXT_EOF +# Implementation Context - VERSION $VERSION_ID + +## ⚠️ AGENTS MUST READ THIS FILE BEFORE EVERY TASK + +--- + +## 1. GENERATED TYPES (Source of Truth) + +### Model Types (types/types.ts) +```typescript +$(cat types/types.ts | grep -A 5 "^export interface" | head -50) +``` + +### Component Props (types/component-props.ts) +```typescript +$(cat types/component-props.ts | grep -A 10 "^export interface" | head -80) +``` + +### API Types (types/api-types.ts) +```typescript +$(cat types/api-types.ts | grep -A 5 "^export interface" | head -50) +``` + +--- + +## 2. MANDATORY IMPORT PATTERNS + +### For Components: +```typescript +// ✅ CORRECT - Always import from generated types +import type { ComponentNameProps } from '@/types/component-props'; +import type { Song, Album, Artist } from '@/types'; + +// ❌ WRONG - Never define your own interfaces +interface ComponentNameProps { ... } +``` + +### For API Routes: +```typescript +// ✅ CORRECT - Always import from generated types +import type { CreateSongRequest, CreateSongResponse } from '@/types/api-types'; +import type { Song } from '@/types'; + +// ❌ WRONG - Never define your own types +type CreateSongRequest = { ... } +``` + +--- + +## 3. PROP STRUCTURE RULES + +### Object Props (CORRECT): +```typescript +// Design says: song: Song +function SongCard({ song, showArtist, onPlay }: SongCardProps) { + return
{song.title} by {song.artist?.name}
; +} +``` + +### Flat Props (WRONG - DO NOT DO THIS): +```typescript +// ❌ WRONG - Flattening the Song object +function SongCard({ id, title, artistName }: SongCardProps) { + return
{title} by {artistName}
; +} +``` + +--- + +## 4. EVENT IMPLEMENTATION + +All events from design MUST be implemented: +```typescript +// If design specifies: onPlay, onAddToPlaylist, onShare +function SongCard({ song, onPlay, onAddToPlaylist, onShare }: SongCardProps) { + return ( +
+ + + +
+ ); +} +``` + +--- + +## 5. REFERENCE FILES + +Before implementing ANY task, read: +1. This file: .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md +2. Task file: .workflow/versions/$VERSION_ID/tasks/task_.yml +3. Context file: .workflow/versions/$VERSION_ID/contexts/.yml +4. Generated types: types/types.ts, types/component-props.ts, types/api-types.ts + +--- + +## 6. VALIDATION COMMAND + +After implementing, always run: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +``` + +CONTEXT_EOF +``` + +**Display confirmation:** +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📋 IMPLEMENTATION CONTEXT GENERATED ║ +╠══════════════════════════════════════════════════════════════╣ +║ File: .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md║ +║ ║ +║ ⚠️ ALL AGENTS MUST READ THIS FILE BEFORE EVERY TASK ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 3.5.5: Transition to IMPLEMENTING [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPLEMENTING +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 4: IMPLEMENTING (Team-Based Parallel Execution) +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = IMPLEMENTING (verified) +**Exit Condition**: Both teams complete, build passes, API contract validated + +**TEAM-BASED PARALLELISM**: Two specialized agents run in parallel: +- **Backend Team**: Implements models + APIs (Layer 1 + Layer 2) +- **Frontend Team**: Implements components + pages (Layer 3) + +Both teams share the same API contract (`app/types/api.ts`) ensuring type safety. + +#### Step 4.1: Verify Phase State [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` +**BLOCK IF**: Phase is not IMPLEMENTING + +#### Step 4.2: Load Contract and Tasks [MANDATORY] +```bash +# Read API contract +cat .workflow/versions/$VERSION_ID/contracts/api_contract.yml + +# Verify shared types exist +ls app/types/api.ts + +# Count backend and frontend tasks +BACKEND_TASKS=$(grep -l 'agent: backend' .workflow/versions/$VERSION_ID/tasks/*.yml 2>/dev/null | wc -l) +FRONTEND_TASKS=$(grep -l 'agent: frontend' .workflow/versions/$VERSION_ID/tasks/*.yml 2>/dev/null | wc -l) +echo "Backend tasks: $BACKEND_TASKS" +echo "Frontend tasks: $FRONTEND_TASKS" +``` + +#### Step 4.3: Launch Both Teams IN PARALLEL [MANDATORY] +**CRITICAL**: Launch BOTH agents in a SINGLE message with TWO Task tool calls. +This enables true parallel execution. + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🚀 LAUNCHING PARALLEL TEAMS ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ ┌─────────────────┐ ┌─────────────────┐ ║ +║ │ BACKEND TEAM │ || │ FRONTEND TEAM │ ║ +║ │ │ || │ │ ║ +║ │ • Models │ || │ • Components │ ║ +║ │ • APIs │ || │ • Pages │ ║ +║ │ │ || │ │ ║ +║ │ Exports: │ || │ Imports: │ ║ +║ │ /api/* routes │ ──→→ ──→│ app/types/api │ ║ +║ └─────────────────┘ └─────────────────┘ ║ +║ ║ +║ SHARED CONTRACT: app/types/api.ts ║ +║ ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +##### Step 4.3.1: Spawn Backend Team Agent [IN PARALLEL] +``` +Use Task tool with: + subagent_type: "backend-implementer" + prompt: | + # BACKEND TEAM - VERSION $VERSION_ID + + ## AGENT INSTRUCTIONS + Read and follow: .claude/agents/backend-implementer.md + + ## YOUR MISSION + Implement ALL backend tasks (models + APIs) for this workflow. + You own the entire backend layer. + + ## 🔴 MANDATORY: READ CONTEXT BEFORE EVERY TASK + + ### FIRST - Read Implementation Context (DO THIS NOW) + ```bash + # READ THIS FILE FIRST - Contains all rules and type definitions + cat .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md + ``` + + This file contains: + - All generated type interfaces + - Mandatory import patterns + - Prop structure rules + - Validation requirements + + ### THEN - For EACH task, read these files: + ```bash + # 1. Task definition + cat .workflow/versions/$VERSION_ID/tasks/task_.yml + + # 2. Entity context with full schema + cat .workflow/versions/$VERSION_ID/contexts/.yml + + # 3. Generated types (verify before implementing) + cat types/types.ts + cat types/api-types.ts + ``` + + ## ⚠️ CRITICAL: TYPE COMPLIANCE (MANDATORY) + + ### Import Types in ALL Files + ```typescript + // ✅ CORRECT - Import from generated types + import type { User, Song, Album } from '@/types'; + import type { CreateUserRequest, CreateUserResponse } from '@/types/api-types'; + + // ❌ WRONG - Do NOT define your own types + interface User { ... } // FORBIDDEN + ``` + + ### Match Design Exactly + - Field names MUST match design document + - Types MUST match generated types + - All fields from design MUST be implemented + + ## TASK FILES + Read all backend tasks: + ```bash + ls .workflow/versions/$VERSION_ID/tasks/task_*model*.yml + ls .workflow/versions/$VERSION_ID/tasks/task_*api*.yml + ``` + + ## IMPLEMENTATION ORDER + 1. **First**: Implement ALL models (Layer 1) + - READ context file FIRST for field definitions + - Add to prisma/schema.prisma matching design EXACTLY + - Run: npx prisma generate + + 2. **Then**: Implement ALL API endpoints (Layer 2) + - READ context file FIRST for request/response schemas + - Create app/api/*/route.ts files + - Import types from @/types and @/types/api-types + - Implement request validation matching design + - Return responses matching generated types + + ## FOR EACH TASK (MANDATORY SEQUENCE) + 1. ⚠️ READ .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md + 2. READ task file: .workflow/versions/$VERSION_ID/tasks/task_*.yml + 3. READ context file: .workflow/versions/$VERSION_ID/contexts/.yml + 4. VERIFY generated types: types/types.ts, types/api-types.ts + 5. IMPLEMENT following the design EXACTLY + 6. VERIFY with TypeScript + + ## VERIFICATION + After all tasks: + ```bash + npx prisma generate 2>&1 || true + npx tsc --noEmit 2>&1 || true + python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist + ``` + + ## OUTPUT FORMAT + ``` + === BACKEND TEAM COMPLETE === + + Context files read: + - IMPLEMENTATION_CONTEXT.md ✓ + - X task files ✓ + - X context files ✓ + + Models implemented: + - model_xxx: prisma/schema.prisma ✓ + + APIs implemented: + - api_xxx: app/api/.../route.ts ✓ + - api_yyy: app/api/.../route.ts ✓ + + Type compliance: + - Types imported from @/types ✓ + - All fields match design ✓ + + TypeScript: PASS/FAIL + Prisma: PASS/FAIL + Validation: PASS/FAIL + ``` +``` + +##### Step 4.3.2: Spawn Frontend Team Agent [IN PARALLEL] +``` +Use Task tool with: + subagent_type: "frontend-implementer" + prompt: | + # FRONTEND TEAM - VERSION $VERSION_ID + + ## AGENT INSTRUCTIONS + Read and follow: .claude/agents/frontend-implementer.md + + ## YOUR MISSION + Implement ALL frontend tasks (components + pages) for this workflow. + You own the entire frontend layer. + + ## 🔴 MANDATORY: READ CONTEXT BEFORE EVERY TASK + + ### FIRST - Read Implementation Context (DO THIS NOW) + ```bash + # READ THIS FILE FIRST - Contains all rules and type definitions + cat .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md + ``` + + This file contains: + - All generated type interfaces + - Mandatory import patterns + - Prop structure rules (object vs flat) + - Event implementation requirements + - Validation requirements + + ### THEN - For EACH task, read these files: + ```bash + # 1. Task definition + cat .workflow/versions/$VERSION_ID/tasks/task_.yml + + # 2. Entity context with props, events, and component hierarchy + cat .workflow/versions/$VERSION_ID/contexts/.yml + + # 3. Generated types (verify before implementing) + cat types/component-props.ts + cat types/types.ts + ``` + + ## ⚠️ CRITICAL: TYPE COMPLIANCE (MANDATORY) + + ### Import Types in ALL Files + ```typescript + // ✅ CORRECT - Import from generated types + import type { SongCardProps } from '@/types/component-props'; + import type { Song, Album, Artist } from '@/types'; + + // ❌ WRONG - Do NOT define your own props + interface SongCardProps { ... } // FORBIDDEN + ``` + + ### Use Object Props (NOT Flat Props) + ```typescript + // ✅ CORRECT - Use typed object from design + function SongCard({ song, showArtist, onPlay }: SongCardProps) { + return
{song.title} by {song.artist?.name}
; + } + + // ❌ WRONG - Flattening the object + function SongCard({ id, title, artistName }: SongCardProps) { + return
{title} by {artistName}
; + } + ``` + + ### Implement ALL Events from Design + Check component-props.ts for event handlers and implement ALL of them. + + ## TASK FILES + Read all frontend tasks: + ```bash + ls .workflow/versions/$VERSION_ID/tasks/task_*component*.yml + ls .workflow/versions/$VERSION_ID/tasks/task_*page*.yml + ``` + + ## FOR EACH TASK (MANDATORY SEQUENCE) + 1. ⚠️ READ .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md + 2. READ task file: .workflow/versions/$VERSION_ID/tasks/task_*.yml + 3. READ context file: .workflow/versions/$VERSION_ID/contexts/.yml + 4. VERIFY generated types: types/component-props.ts, types/types.ts + 5. CHECK props interface - note ALL props and events + 6. IMPLEMENT following the design EXACTLY + 7. VERIFY all events are implemented + + ## IMPLEMENTATION CHECKLIST (FOR EACH COMPONENT) + - [ ] Read IMPLEMENTATION_CONTEXT.md + - [ ] Read context file for props and events + - [ ] Import props interface from @/types/component-props + - [ ] Import model types from @/types + - [ ] Use object props (song: Song, not id, title, ...) + - [ ] Implement ALL event handlers from design + - [ ] Destructure props matching design EXACTLY + + ## VERIFICATION + After all tasks: + ```bash + npx tsc --noEmit 2>&1 || true + python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist + ``` + + ## OUTPUT FORMAT + ``` + === FRONTEND TEAM COMPLETE === + + Context files read: + - IMPLEMENTATION_CONTEXT.md ✓ + - X task files ✓ + - X context files ✓ + + Components implemented: + - component_xxx: app/components/Xxx.tsx ✓ + Props: Imported from @/types/component-props ✓ + Events: onPlay, onAddToPlaylist ✓ + + Pages implemented: + - page_xxx: app/xxx/page.tsx ✓ + + Type compliance: + - Types imported from @/types ✓ + - Object props used (not flat) ✓ + - All events implemented ✓ + + TypeScript: PASS/FAIL + Validation: PASS/FAIL + ``` +``` + +**IMPORTANT**: You MUST send BOTH Task tool calls in a SINGLE message. +This enables parallel execution. Do NOT wait for one to complete. + +#### Step 4.4: Wait for Both Teams [MANDATORY] +**MUST wait for BOTH agents to complete before proceeding.** + +Display progress: +``` +╔══════════════════════════════════════════════════════════════╗ +║ ⏳ TEAMS EXECUTING... ║ +╠══════════════════════════════════════════════════════════════╣ +║ Backend Team: 🔄 In Progress ║ +║ Frontend Team: 🔄 In Progress ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 4.5: Implementation Validation [MANDATORY] +**Run implementation validation with checklist to verify design compliance:** + +```bash +# Run implementation validator +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py validate --checklist +VALIDATE_EXIT=$? +echo "VALIDATE_EXIT=$VALIDATE_EXIT" +``` + +**Validation checks:** +- Components import from @/types/component-props +- Components use object props (not flat props) +- All events from design are implemented +- API routes exist and match design +- Prisma models match design fields + +**BLOCK IF**: VALIDATE_EXIT != 0 → Show errors and halt + +#### Step 4.6: Post-Implementation Verification [MANDATORY] +```bash +# Verify build passes +npm run build +echo "Exit code: $?" +``` +**BLOCK IF**: Exit code != 0 → Error with build output + +```bash +# Verify all task files have corresponding implementation files +for task in .workflow/versions/$VERSION_ID/tasks/*.yml; do + grep "to_create:" -A 10 "$task" | grep -E "^\s+-" | sed 's/.*- //' | while read path; do + if [ ! -f "$path" ]; then + echo "MISSING: $path" + fi + done +done +``` +**BLOCK IF**: Any file MISSING → List missing files, halt workflow + +#### Step 4.6.1: Display Implementation Checklist [MANDATORY] +```bash +# Show the implementation checklist +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py checklist show +``` + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 📋 IMPLEMENTATION CHECKLIST ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ COMPONENTS ║ +║ ✅ component_song_card - types imported, events ok ║ +║ ✅ component_album_card - types imported, events ok ║ +║ ❌ component_player - missing onSeek event ║ +║ ║ +║ APIs ║ +║ ✅ api_get_songs - route exists, methods ok ║ +║ ✅ api_create_song - route exists, methods ok ║ +║ ║ +║ MODELS ║ +║ ✅ model_song - all fields match design ║ +║ ✅ model_album - all fields match design ║ +║ ║ +╠══════════════════════════════════════════════════════════════╣ +║ Errors: X | Warnings: Y | Passed: Z ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +**BLOCK IF**: Any errors in checklist → Fix errors before proceeding + +#### Step 4.7: Validate API Contract Compliance [MANDATORY] +```bash +# Run contract validation +python3 skills/guardrail-orchestrator/scripts/validate_against_contract.py \ + .workflow/versions/$VERSION_ID/contracts/api_contract.yml \ + --project-dir . +CONTRACT_EXIT=$? +echo "CONTRACT_EXIT=$CONTRACT_EXIT" +``` + +**Validates:** +- All backend routes exist and match contract methods +- All frontend components import from shared types +- All API calls use correct paths and methods + +**BLOCK IF**: CONTRACT_EXIT != 0 → Display violations, halt workflow + +#### Step 4.8: Display Implementation Summary [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ PARALLEL IMPLEMENTATION COMPLETE ║ +╠══════════════════════════════════════════════════════════════╣ +║ Backend Team: ║ +║ Models: X implemented ║ +║ APIs: X implemented ║ +║ ║ +║ Frontend Team: ║ +║ Components: X implemented ║ +║ Pages: X implemented ║ +╠══════════════════════════════════════════════════════════════╣ +║ API Contract: VALID ✓ ║ +║ Shared Types: app/types/api.ts ✓ ║ +║ Build: PASS ✓ ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 4.9: Transition [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition INTEGRATING +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 4.5: INTEGRATING (Connect New Features to Existing Project) +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = INTEGRATING (verified) +**Exit Condition**: New features integrated with existing project, navigation/imports/wiring complete + +**PURPOSE**: This phase ensures newly implemented components, pages, and APIs are +actually usable in the application - not just isolated implementations. + +#### Step 4.5.1: Verify Phase State [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` +**BLOCK IF**: Phase is not INTEGRATING + +#### Step 4.5.2: Discover Existing Project Structure [MANDATORY] +```bash +# Find navigation files +echo "=== Navigation Files ===" +find app components -name '*nav*' -o -name '*sidebar*' -o -name '*menu*' 2>/dev/null | head -10 + +# Find layout files +echo "=== Layout Files ===" +find app -name 'layout.tsx' 2>/dev/null + +# Find existing routes/pages +echo "=== Existing Pages ===" +find app -name 'page.tsx' 2>/dev/null | head -20 +``` + +#### Step 4.5.3: Spawn Integrator Agent [MANDATORY] +**CRITICAL**: The integrator agent connects new implementations to the existing project. + +``` +Use Task tool with: + subagent_type: "general-purpose" + prompt: | + # INTEGRATOR AGENT - VERSION $VERSION_ID + + ## AGENT INSTRUCTIONS + Read and follow: .claude/agents/integrator.yml + + ## YOUR MISSION + Connect newly implemented components, pages, and APIs with the existing project. + Without integration, new features are useless - they exist but users can't access them. + + ## 🔴 MANDATORY: READ CONTEXT FIRST + ```bash + cat .workflow/versions/$VERSION_ID/IMPLEMENTATION_CONTEXT.md + cat .workflow/versions/$VERSION_ID/design/design_document.yml + ``` + + ## INTEGRATION TASKS + + ### 1. NAVIGATION INTEGRATION + Find where navigation/sidebar is defined and add links to new pages: + + ```bash + # Find navigation components + grep -r "navItems\|menuItems\|navigation" app/ components/ --include="*.tsx" -l + ``` + + For each new page in design_document.yml: + - Add a navigation link/menu item + - Use the correct path from design + - Import any needed icons + + ### 2. COMPONENT USAGE INTEGRATION + Import and use new components in appropriate places: + + For each new component: + - Identify which pages should use it (from design data_needs) + - Import the component + - Add to JSX in appropriate location + - Pass required props + + ### 3. API WIRING + Connect new APIs to frontend components: + + For pages with data_needs in design: + - Add useSWR/fetch calls for the API + - Handle loading and error states + - Pass data to components + + ### 4. BARREL EXPORTS + Update index.ts files to export new components: + + ```bash + # Find index files + find components lib -name 'index.ts' 2>/dev/null + ``` + + For each new component: + - Add export to nearest index.ts + + ## DISCOVERY COMMANDS + ```bash + # Find existing navigation structure + grep -rn "href=\|to=" app/ components/ --include="*.tsx" | head -20 + + # Find existing data fetching patterns + grep -rn "useSWR\|useQuery\|fetch(" app/ --include="*.tsx" | head -20 + + # Find existing component imports + grep -rn "from '@/components" app/ --include="*.tsx" | head -20 + ``` + + ## OUTPUT FORMAT + ``` + === INTEGRATION COMPLETE === + + NAVIGATION UPDATES: + ✅ app/layout.tsx: Added link to /songs + ✅ components/Sidebar.tsx: Added Songs menu item + + COMPONENT INTEGRATIONS: + ✅ app/dashboard/page.tsx: Imported and used SongList + ✅ app/songs/page.tsx: Wired to /api/songs + + API WIRING: + ✅ app/songs/page.tsx: Added useSWR('/api/songs') + + BARREL EXPORTS: + ✅ components/index.ts: Added SongCard, SongList exports + + TypeScript: PASS/FAIL + Build: PASS/FAIL + ``` +``` + +#### Step 4.5.4: Validate Integration [MANDATORY] +```bash +# Run integration validator +python3 skills/guardrail-orchestrator/scripts/validate_integration.py \ + --design-doc .workflow/versions/$VERSION_ID/design/design_document.yml \ + --project-dir . +INTEGRATION_EXIT=$? +echo "INTEGRATION_EXIT=$INTEGRATION_EXIT" +``` + +**Validates:** +- New pages are accessible via navigation +- New components are imported and used +- New APIs are called from frontend +- Barrel exports are updated + +**BLOCK IF**: INTEGRATION_EXIT = 2 (critical failures) +**WARNING IF**: INTEGRATION_EXIT = 1 (warnings) + +#### Step 4.5.5: Verify Build Still Passes [MANDATORY] +```bash +npm run build +BUILD_EXIT=$? +echo "BUILD_EXIT=$BUILD_EXIT" +``` +**BLOCK IF**: Build fails after integration changes + +#### Step 4.5.6: Display Integration Summary [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🔗 INTEGRATION COMPLETE ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ NAVIGATION: ║ +║ ✅ /songs added to sidebar ║ +║ ✅ /albums added to sidebar ║ +║ ║ +║ COMPONENT USAGE: ║ +║ ✅ SongCard used in 2 files ║ +║ ✅ AlbumCard used in 1 file ║ +║ ║ +║ API WIRING: ║ +║ ✅ /api/songs called from app/songs/page.tsx ║ +║ ✅ /api/albums called from app/albums/page.tsx ║ +║ ║ +║ BUILD: PASS ✓ ║ +║ TYPESCRIPT: PASS ✓ ║ +║ ║ +╠══════════════════════════════════════════════════════════════╣ +║ New features are now accessible in the application! ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 4.5.7: Transition to Review [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition REVIEWING +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 5: REVIEWING +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = REVIEWING (verified) +**Exit Condition**: All checks pass, ready for approval + +#### Step 5.1: Verify Phase State [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` +**BLOCK IF**: Phase is not REVIEWING + +#### IF AUTO_MODE = true: Automated Review + +##### Step 5.2a: Run Build Validation [MANDATORY] +```bash +npm run build 2>&1 +BUILD_EXIT=$? +``` + +##### Step 5.2b: Generate Implementation Visualization [MANDATORY] +**MUST show user what was built before review:** + +```bash +python3 skills/guardrail-orchestrator/scripts/visualize_implementation.py --manifest project_manifest.json +``` + +This displays: +- 📱 Page structure with routes +- 🧩 Component hierarchy tree +- 🔌 API endpoints with methods +- 📊 Implementation statistics (lines, hooks, types) + +##### Step 5.3a: Check All Files Exist [MANDATORY] +```bash +MISSING_COUNT=0 +for task in .workflow/versions/$VERSION_ID/tasks/*.yml; do + grep "file_paths:" -A 20 "$task" | grep -E "^\s+-\s+" | sed 's/.*- //' | while read path; do + if [ ! -f "$path" ]; then + echo "MISSING: $path" + MISSING_COUNT=$((MISSING_COUNT + 1)) + fi + done +done +echo "Missing files: $MISSING_COUNT" +``` + +##### Step 5.4a: Auto-Decision [MANDATORY] +``` +IF BUILD_EXIT = 0 AND MISSING_COUNT = 0: + → Display: "✅ AUTO-REVIEW PASSED" + → Run: python3 .../workflow_manager.py transition SECURITY_REVIEW + → Proceed to Phase 5.5 (Security Review) +ELSE: + → Display: "❌ AUTO-REVIEW FAILED" + → List all failures (build errors, missing files) + → HALT workflow + → Output: "Fix issues and run /workflow:resume" +``` + +##### Step 5.5a: Auto Review Report [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🔍 AUTO REVIEW RESULTS ║ +╠══════════════════════════════════════════════════════════════╣ +║ Build: PASS / FAIL ║ +║ Files: X/Y exist ║ +║ Decision: AUTO-APPROVED / AUTO-REJECTED ║ +╠══════════════════════════════════════════════════════════════╣ +║ IMPLEMENTATION SUMMARY ║ +║ Pages: X implemented ║ +║ Components: X implemented ║ +║ API Endpoints: X implemented ║ +║ Total Lines: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ 👆 See visualization above for details ║ +╠══════════════════════════════════════════════════════════════╣ +║ [If failed, list specific failures here] ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +#### IF AUTO_MODE = false: Agent Review + +##### Step 5.2b: Generate Implementation Visualization [MANDATORY] +**MUST show user what was built before spawning reviewer:** + +```bash +python3 skills/guardrail-orchestrator/scripts/visualize_implementation.py --manifest project_manifest.json +``` + +This shows the user: +- Pages with their routes and components +- Component hierarchy and relationships +- API endpoints with HTTP methods +- Code statistics and metrics + +##### Step 5.3b: Spawn Reviewer Agent [MANDATORY] +``` +Use Task tool with: + subagent_type: "workflow-validator" + prompt: | + # REVIEWER AGENT - VERSION $VERSION_ID + + ## AGENT INSTRUCTIONS + Read and follow: .claude/agents/workflow-validator.md + + ## STRICT REQUIREMENTS + Review ALL tasks. Report ALL issues. No partial reviews. + + ## REVIEW CHECKLIST (FOR EACH TASK) + + Task files: .workflow/versions/$VERSION_ID/tasks/*.yml + + For each task: + 1. [ ] Read task file + 2. [ ] Verify ALL file_paths exist + 3. [ ] Read each file, verify: + - [ ] Exports match manifest + - [ ] Types are correct + - [ ] No TypeScript errors + - [ ] Follows project patterns + 4. [ ] Check acceptance_criteria met + + ## VALIDATION (RUN THESE) + ```bash + npm run build + npm run lint 2>/dev/null || echo "No lint configured" + ``` + + ## OUTPUT FORMAT (REQUIRED) + ``` + === REVIEW REPORT === + + TASK: task_create_xxx + - Files: EXIST / MISSING (list) + - Build: PASS / FAIL + - Quality: PASS / ISSUES (list) + - Verdict: APPROVED / NEEDS_CHANGES + + [Repeat for each task] + + SUMMARY + - Total tasks: X + - Approved: X + - Need changes: X + - Overall: PASS / FAIL + ``` +``` + +##### Step 5.3b: Transition to Security Review [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition SECURITY_REVIEW +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 5.5: SECURITY REVIEW +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = SECURITY_REVIEW (verified) +**Exit Condition**: Security scan passes (no CRITICAL issues), ready for approval + +#### Step 5.5.1: Verify Phase State [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` +**BLOCK IF**: Phase is not SECURITY_REVIEW + +#### Step 5.5.2: Run Security Scanner [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/security_scan.py \ + --project-dir . \ + --severity HIGH +SECURITY_EXIT=$? +echo "SECURITY_EXIT=$SECURITY_EXIT" +``` + +**Exit codes:** +- 0 = PASS (no critical/high issues) +- 1 = HIGH issues found (warning in normal mode, blocks in --strict) +- 2 = CRITICAL issues found (always blocks) + +#### Step 5.5.3: API Contract Validation [MANDATORY] +```bash +# Validate against generated contract (types and routes) +python3 skills/guardrail-orchestrator/scripts/validate_against_contract.py \ + .workflow/versions/$VERSION_ID/contracts/api_contract.yml \ + --project-dir . +CONTRACT_EXIT=$? +echo "CONTRACT_EXIT=$CONTRACT_EXIT" + +# Also run static API analysis +python3 skills/guardrail-orchestrator/scripts/validate_api_contract.py \ + --project-dir . +API_EXIT=$? +echo "API_EXIT=$API_EXIT" +``` + +**Contract Validation Checks:** +- Backend routes exist and export correct HTTP methods +- Frontend files import from shared types (@/types/api) +- Types not recreated locally (must use shared types) + +**Static API Validation Checks:** +- Frontend API calls have matching backend endpoints +- HTTP methods match (GET, POST, PUT, DELETE) +- Request bodies are sent where expected + +#### Step 5.5.4: Security Decision [MANDATORY] + +##### IF AUTO_MODE = true: +``` +IF SECURITY_EXIT = 0 AND API_EXIT = 0 AND CONTRACT_EXIT = 0: + → Display: "✅ SECURITY & CONTRACT VALIDATION PASSED" + → Transition to AWAITING_IMPL_APPROVAL +ELSE IF SECURITY_EXIT = 2: + → Display: "❌ CRITICAL SECURITY ISSUES - BLOCKING" + → List all critical issues + → Transition to IMPLEMENTING (must fix) + → HALT workflow +ELSE IF CONTRACT_EXIT = 2: + → Display: "❌ CONTRACT VIOLATIONS - BLOCKING" + → List all contract violations + → Transition to IMPLEMENTING (must fix) + → HALT workflow +ELSE IF SECURITY_EXIT = 1 OR CONTRACT_EXIT = 1: + → Display: "⚠️ HIGH SEVERITY ISSUES - WARNING" + → List high severity issues and contract warnings + → Continue to approval (warning only in auto mode) +``` + +##### IF AUTO_MODE = false: +Use AskUserQuestion: +``` +Question: "Security scan found issues. How do you want to proceed?" +Options: + 1. "Accept risks - Continue to approval" (if no CRITICAL) + 2. "Fix issues - Return to implementation" + 3. "Run full audit - /workflow:security --full" +``` + +#### Step 5.5.5: Display Security Report [MANDATORY] +``` +╔══════════════════════════════════════════════════════════════╗ +║ 🔒 SECURITY & CONTRACT REVIEW RESULTS ║ +╠══════════════════════════════════════════════════════════════╣ +║ Security Scan: PASS / WARNING / CRITICAL ║ +║ Critical: X issues ║ +║ High: X issues ║ +║ Medium: X issues ║ +║ Low: X issues ║ +╠══════════════════════════════════════════════════════════════╣ +║ Contract Compliance: PASS / WARNING / FAIL ║ +║ Backend routes: X of X verified ║ +║ Type imports: X files checked ║ +║ Violations: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ API Static Analysis: PASS / FAIL ║ +║ Matched calls: X ║ +║ Unmatched: X ║ +║ Method errors: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ [If issues found, list top 5 with file locations] ║ +╠══════════════════════════════════════════════════════════════╣ +║ VERDICT: APPROVED / NEEDS_FIXES ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +#### Step 5.5.6: Transition [MANDATORY] +```bash +# If security passed +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition AWAITING_IMPL_APPROVAL + +# If security failed (CRITICAL issues) +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition IMPLEMENTING +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 6: GATE 2 - Implementation Approval +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = AWAITING_IMPL_APPROVAL +**Exit Condition**: Implementation approved, phase = COMPLETING + +**Note**: In AUTO_MODE, this gate is handled in Phase 5 + +#### IF AUTO_MODE = false: +Use AskUserQuestion: +``` +Question: "Review complete. How do you want to proceed?" +Options: + 1. "Approve - Mark as complete" + 2. "Reject - Request fixes" + 3. "Pause - Save and exit" +``` + +**On Approve**: +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py approve implementation +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py transition COMPLETING +``` + +**On Reject**: Provide feedback, return to Phase 4 +**On Pause**: Output resume command and stop + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 7: COMPLETING +### ═══════════════════════════════════════════════════════════════ + +**Entry Condition**: Phase = COMPLETING (verified) +**Exit Condition**: Version marked complete, success report displayed + +#### Step 7.1: Verify Phase State [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` +**BLOCK IF**: Phase is not COMPLETING + +#### Step 7.2: Update Task Statuses [MANDATORY] +```bash +# Mark all tasks as completed +for task in .workflow/versions/$VERSION_ID/tasks/*.yml; do + sed -i '' 's/status: .*/status: completed/' "$task" 2>/dev/null || \ + sed -i 's/status: .*/status: completed/' "$task" +done +``` + +#### Step 7.3: Update Manifest Statuses [MANDATORY] +Update all entities referenced in tasks from "PENDING" to "IMPLEMENTED" + +#### Step 7.4: Complete Version [MANDATORY] +```bash +python3 skills/guardrail-orchestrator/scripts/version_manager.py complete +``` +**VERIFY**: Script exits with code 0 + +#### Step 7.5: Final Report [MANDATORY] + +**IF AUTO_MODE = true**: Display completion-only report (NO next steps) +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ WORKFLOW COMPLETED (AUTO) ║ +╠══════════════════════════════════════════════════════════════╣ +║ Version: $VERSION_ID ║ +║ Feature: $FEATURE ║ +╠══════════════════════════════════════════════════════════════╣ +║ SUMMARY ║ +║ Tasks completed: X ║ +║ Files created: X ║ +║ Files modified: X ║ +║ Build: PASS ║ +╚══════════════════════════════════════════════════════════════╝ +``` +**DO NOT** include "NEXT STEPS" in AUTO mode - the workflow is complete. + +**IF AUTO_MODE = false**: Display full report with next steps +``` +╔══════════════════════════════════════════════════════════════╗ +║ ✅ WORKFLOW COMPLETED ║ +╠══════════════════════════════════════════════════════════════╣ +║ Version: $VERSION_ID ║ +║ Feature: $FEATURE ║ +║ Mode: INTERACTIVE ║ +╠══════════════════════════════════════════════════════════════╣ +║ SUMMARY ║ +║ Tasks completed: X ║ +║ Files created: X ║ +║ Files modified: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ NEXT STEPS ║ +║ npm run dev Test the feature ║ +║ /workflow:history View all versions ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +--- + +## USAGE + +```bash +# Manual mode (full control, stops at all gates) +/workflow:spawn add user profile page + +# Auto mode (guided discovery with questions, auto-approves gates) +/workflow:spawn --auto add user authentication + +# Full-auto mode (AI expands idea, only asks for acceptance criteria) +/workflow:spawn --full-auto add dark mode toggle +``` + +### Mode Selection Guide + +| Use Case | Recommended Mode | Why | +|----------|------------------|-----| +| Complex feature, unclear requirements | `--auto` | AI guides you through requirements | +| Quick prototype, trust AI judgment | `--full-auto` | Fast, minimal input needed | +| Specific requirements already known | Manual | Full control over every step | +| Learning the workflow | Manual | See all gates and decisions | +| Production feature | `--auto` | Ensures requirements are complete | + +### Examples + +```bash +# Manual - full control +/workflow:spawn add user authentication + +# Auto - AI asks questions until requirements are clear +/workflow:spawn --auto add user authentication +# AI asks: "What auth method?" → "OAuth providers?" → "Password reset?" → etc. + +# Full-auto - AI expands idea, you approve criteria +/workflow:spawn --full-auto add user authentication +# AI expands: "I'll add login, register, password reset, OAuth, profile..." +# AI asks: "What are your acceptance criteria?" +``` + +--- + +## ERROR RECOVERY + +| Error | Command | +|-------|---------| +| Workflow interrupted | `/workflow:resume` | +| Check current state | `/workflow:status` | +| View history | `/workflow:history` | +| Skip to specific phase | Not allowed - must follow sequence | + +--- + +## ENFORCEMENT CHECKLIST + +Before completing this command, verify: +- [ ] Version created with version_manager.py +- [ ] Phase transitions logged with workflow_manager.py +- [ ] Task files exist in .workflow/versions/$VERSION_ID/tasks/ +- [ ] Build passes (exit code 0) +- [ ] All file_paths in tasks exist +- [ ] Final report displayed diff --git a/.claude/commands/workflow/status.md b/.claude/commands/workflow/status.md new file mode 100644 index 0000000..d3723fa --- /dev/null +++ b/.claude/commands/workflow/status.md @@ -0,0 +1,119 @@ +--- +description: Show workflow status and task summary +allowed-tools: Read, Bash +--- + +# Workflow Status + +Display current workflow status and task breakdown. + +## Steps + +### 1. Check Active Workflow +```bash +python3 skills/guardrail-orchestrator/scripts/workflow_manager.py status +``` + +If active workflow exists, display workflow state. +If no workflow, continue with manual task scan. + +### 2. Read Project Manifest +Check `project_manifest.json` for: +- Current phase +- Entity counts by status + +### 3. Scan Tasks +Get the version-specific tasks directory: +```bash +TASKS_DIR=$(python3 skills/guardrail-orchestrator/scripts/version_manager.py tasks-dir) +``` + +Read all `$TASKS_DIR/*.yml` files and count by: +- Status (pending, in_progress, review, approved, completed, blocked) +- Agent (frontend, backend, reviewer) +- Type (create, update, delete, review) + +### 4. Display Summary + +``` +╔══════════════════════════════════════════════════════════════╗ +║ WORKFLOW STATUS ║ +╠══════════════════════════════════════════════════════════════╣ +║ Active Workflow: | None ║ +║ Feature: ║ +║ Phase: ║ +╠══════════════════════════════════════════════════════════════╣ +║ APPROVAL GATES ║ +║ 🛑 Design: ║ +║ 🛑 Implementation: ║ +╠══════════════════════════════════════════════════════════════╣ +║ TASKS BY STATUS ║ +║ ⏳ Pending: X ║ +║ 🔄 In Progress: X ║ +║ 🔍 Review: X ║ +║ ✅ Approved: X ║ +║ ✓ Completed: X ║ +║ 🚫 Blocked: X ║ +╠══════════════════════════════════════════════════════════════╣ +║ TASKS BY AGENT ║ +║ 🎨 Frontend: X pending, X completed ║ +║ ⚙️ Backend: X pending, X completed ║ +║ 🔍 Reviewer: X pending ║ +╠══════════════════════════════════════════════════════════════╣ +║ NEXT ACTIONS ║ +║ /workflow:frontend --next (X tasks available) ║ +║ /workflow:backend --next (X tasks available) ║ +║ /workflow:review --next (X tasks to review) ║ +║ /workflow:resume (continue workflow) ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +### 5. Show Design Visualization +**If in DESIGNING or AWAITING_DESIGN_APPROVAL phase**, display visual design: + +```bash +python3 skills/guardrail-orchestrator/scripts/visualize_design.py --manifest project_manifest.json +``` + +This shows: +- 📱 Page flow diagram +- 📄 Page details with components +- 🧩 Component hierarchy +- 🔌 API endpoints +- 🔄 Data flow architecture + +### 5b. Show Implementation Visualization +**If in REVIEWING, SECURITY_REVIEW, or AWAITING_IMPL_APPROVAL phase**, display what was built: + +```bash +python3 skills/guardrail-orchestrator/scripts/visualize_implementation.py --manifest project_manifest.json +``` + +This shows: +- 📱 Page structure with routes +- 🧩 Component hierarchy and relationships +- 🔌 API endpoints with HTTP methods +- 📊 Implementation statistics (lines, hooks, types) +- 🌳 Component tree view + +### 6. List Pending Tasks +Show table of tasks ready to work on: + +| Task ID | Type | Agent | Priority | Dependencies | +|---------|------|-------|----------|--------------| + +### 7. Show Approval Instructions + +**If AWAITING_DESIGN_APPROVAL**: +``` +🛑 Design approval required. Review the entities and tasks, then: + - Approve: /workflow:approve design + - Reject: /workflow:reject design "reason" +``` + +**If AWAITING_IMPL_APPROVAL**: +``` +🛑 Implementation approval required. Review the code, then: + - Approve: /workflow:approve implementation + - Reject: /workflow:reject implementation "reason" +``` diff --git a/.claude/references/dockerfile-templates.md b/.claude/references/dockerfile-templates.md new file mode 100644 index 0000000..bea65d3 --- /dev/null +++ b/.claude/references/dockerfile-templates.md @@ -0,0 +1,621 @@ +# Dockerfile Templates Reference + +Comprehensive Dockerfile templates for Eureka platform deployments. Use these templates based on framework detection. + +## Eureka Platform Environment + +**See also:** `eureka-infrastructure.md` for full infrastructure details. + +### Runtime Environment Variables (Injected by Eureka) + +```bash +# Database +DATABASE_URL + +# MinIO +MINIO_ENDPOINT +MINIO_PORT +MINIO_ROOT_USER +MINIO_ROOT_PASSWORD +MINIO_BUCKET_NAME +MINIO_USE_SSL +MINIO_PUBLIC_URL +``` + +**IMPORTANT:** These are runtime variables. Do NOT hardcode in Dockerfile. + +## Framework Detection Logic + +```javascript +function detectFramework(packageJson, files) { + const deps = { ...packageJson?.dependencies, ...packageJson?.devDependencies }; + + // Check for ORMs first (affects base image choice) + const hasPrisma = !!deps['prisma'] || !!deps['@prisma/client']; + const hasDrizzle = !!deps['drizzle-orm']; + + // Frontend/Full-stack + if (deps['next']) return hasPrisma ? 'nextjs-prisma' : 'nextjs'; + if (deps['nuxt']) return 'nuxt'; + if (deps['@remix-run/node']) return 'remix'; + if (deps['@sveltejs/kit']) return 'sveltekit'; + if (deps['vite'] && deps['react']) return 'react-vite'; + if (deps['react-scripts']) return 'react-cra'; + if (deps['vite'] && deps['vue']) return 'vue-vite'; + if (deps['@angular/core']) return 'angular'; + + // Backend Node.js + if (deps['@nestjs/core']) return hasPrisma ? 'nestjs-prisma' : 'nestjs'; + if (deps['fastify']) return hasPrisma ? 'fastify-prisma' : 'fastify'; + if (deps['express']) return hasPrisma ? 'express-prisma' : 'express'; + if (deps['hono']) return 'hono'; + + // Python + if (files['requirements.txt']) { + const reqs = files['requirements.txt']; + if (reqs.includes('fastapi')) return 'fastapi'; + if (reqs.includes('flask')) return 'flask'; + if (reqs.includes('django')) return 'django'; + } + + // Go + if (files['go.mod']) return 'go'; + + // Rust + if (files['Cargo.toml']) return 'rust'; + + return 'unknown'; +} +``` + +## Key Considerations by Framework + +| Framework | Base Image | Special Requirements | Port | +|---------------|------------------------|---------------------------------------|------| +| + Prisma | node:20-alpine3.18 | openssl1.1-compat, dummy DATABASE_URL | - | +| Next.js | node:20-alpine | output: 'standalone' in config | 3000 | +| Nuxt 3 | node:20-alpine | - | 3000 | +| SvelteKit | node:20-alpine | - | 3000 | +| Remix | node:20-alpine | - | 3000 | +| React/Vue SPA | nginx:alpine | nginx.conf for SPA routing | 80 | +| Angular | nginx:alpine | nginx.conf for SPA routing | 80 | +| Express | node:20-alpine | - | 3000 | +| Fastify | node:20-alpine | - | 3000 | +| NestJS | node:20-alpine | - | 3000 | +| Hono | node:20-alpine | - | 3000 | +| FastAPI | python:3.12-slim | uvicorn | 8000 | +| Flask | python:3.12-slim | gunicorn | 5000 | +| Django | python:3.12-slim | gunicorn, collectstatic | 8000 | +| Go | golang:alpine → alpine | CGO_ENABLED=0 for static binary | 8080 | +| Rust | rust:alpine → alpine | musl-dev for static linking | 8080 | + +--- + +## Frontend Frameworks + +### React (Vite) + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM nginx:alpine AS runner +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +**Required nginx.conf for SPA:** +```nginx +server { + listen 80; + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } +} +``` + +### React (Create React App) + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM nginx:alpine AS runner +COPY --from=builder /app/build /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +### Vue.js (Vite) + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM nginx:alpine AS runner +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +### Angular + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build --prod + +FROM nginx:alpine AS runner +COPY --from=builder /app/dist/*/browser /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +### Svelte/SvelteKit + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY --from=builder /app/build ./build +COPY --from=builder /app/package.json ./ +RUN npm ci --omit=dev +EXPOSE 3000 +CMD ["node", "build"] +``` + +--- + +## Full-Stack Frameworks + +### Next.js (App Router) - Basic + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +ENV NEXT_TELEMETRY_DISABLED=1 +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +USER nextjs +EXPOSE 3000 +ENV PORT=3000 HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] +``` + +**Required next.config.js:** +```javascript +module.exports = { output: 'standalone' } +``` + +### Next.js + Prisma + +```dockerfile +FROM node:20-alpine3.18 AS base + +FROM base AS deps +RUN apk add --no-cache libc6-compat openssl1.1-compat +WORKDIR /app +COPY package.json package-lock.json* ./ +COPY prisma ./prisma/ +RUN npm ci +ENV DATABASE_URL="file:./placeholder.db" +RUN npx prisma generate + +FROM base AS builder +RUN apk add --no-cache openssl1.1-compat +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +ENV DATABASE_URL="file:./placeholder.db" +RUN npx prisma generate +ENV NEXT_TELEMETRY_DISABLED=1 +RUN npm run build + +FROM base AS runner +RUN apk add --no-cache openssl1.1-compat +WORKDIR /app +ENV NODE_ENV=production +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma +USER nextjs +EXPOSE 3000 +ENV PORT=3000 HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] +``` + +### Nuxt.js 3 + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY --from=builder /app/.output ./ +EXPOSE 3000 +CMD ["node", ".output/server/index.mjs"] +``` + +### Remix + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/build ./build +COPY --from=builder /app/public ./public +COPY --from=builder /app/package.json ./ +EXPOSE 3000 +CMD ["npm", "start"] +``` + +--- + +## Backend Frameworks + +### Express.js / Node.js + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build 2>/dev/null || true + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist 2>/dev/null || true +COPY --from=builder /app/src ./src 2>/dev/null || true +COPY --from=builder /app/*.js ./ 2>/dev/null || true +EXPOSE 3000 +CMD ["node", "dist/index.js"] +``` + +### Express + Prisma + +```dockerfile +FROM node:20-alpine3.18 AS builder +RUN apk add --no-cache openssl1.1-compat +WORKDIR /app +COPY package.json package-lock.json* ./ +COPY prisma ./prisma/ +RUN npm ci +ENV DATABASE_URL="file:./placeholder.db" +RUN npx prisma generate +COPY . . +RUN npm run build 2>/dev/null || true + +FROM node:20-alpine3.18 AS runner +RUN apk add --no-cache openssl1.1-compat +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma +EXPOSE 3000 +CMD ["node", "dist/index.js"] +``` + +### Fastify + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist +EXPOSE 3000 +CMD ["node", "dist/index.js"] +``` + +### NestJS + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist +EXPOSE 3000 +CMD ["node", "dist/main.js"] +``` + +### NestJS + Prisma + +```dockerfile +FROM node:20-alpine3.18 AS builder +RUN apk add --no-cache openssl1.1-compat +WORKDIR /app +COPY package.json package-lock.json* ./ +COPY prisma ./prisma/ +RUN npm ci +ENV DATABASE_URL="file:./placeholder.db" +RUN npx prisma generate +COPY . . +RUN npm run build + +FROM node:20-alpine3.18 AS runner +RUN apk add --no-cache openssl1.1-compat +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma +EXPOSE 3000 +CMD ["node", "dist/main.js"] +``` + +### Hono + +```dockerfile +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist +EXPOSE 3000 +CMD ["node", "dist/index.js"] +``` + +--- + +## Python Frameworks + +### FastAPI + +```dockerfile +FROM python:3.12-slim AS builder +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +FROM python:3.12-slim AS runner +WORKDIR /app +COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY . . +EXPOSE 8000 +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +### Flask + +```dockerfile +FROM python:3.12-slim AS builder +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +FROM python:3.12-slim AS runner +WORKDIR /app +COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY . . +EXPOSE 5000 +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"] +``` + +### Django + +```dockerfile +FROM python:3.12-slim AS builder +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +FROM python:3.12-slim AS runner +WORKDIR /app +COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY . . +RUN python manage.py collectstatic --noinput +EXPOSE 8000 +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "project.wsgi:application"] +``` + +--- + +## Other Languages + +### Go (Gin/Echo/Fiber) + +```dockerfile +FROM golang:1.22-alpine AS builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o main . + +FROM alpine:latest AS runner +RUN apk --no-cache add ca-certificates +WORKDIR /app +COPY --from=builder /app/main . +EXPOSE 8080 +CMD ["./main"] +``` + +### Rust (Actix/Axum) + +```dockerfile +FROM rust:1.75-alpine AS builder +RUN apk add --no-cache musl-dev +WORKDIR /app +COPY Cargo.toml Cargo.lock ./ +COPY src ./src +RUN cargo build --release + +FROM alpine:latest AS runner +RUN apk --no-cache add ca-certificates +WORKDIR /app +COPY --from=builder /app/target/release/app . +EXPOSE 8080 +CMD ["./app"] +``` + +--- + +## Supporting Files + +### nginx.conf (for SPAs) + +```nginx +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml; + + # SPA routing - all routes to index.html + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; +} +``` + +### .dockerignore (recommended) + +``` +node_modules +.git +.gitignore +*.md +.env* +.next +dist +build +coverage +.nyc_output +*.log +.DS_Store +Thumbs.db +``` + +--- + +## Eureka Deployment Checklist + +Before generating Dockerfile: + +1. **Detect framework** from package.json/requirements.txt/go.mod/Cargo.toml +2. **Check for ORMs** (Prisma, Drizzle) → use alpine3.18 + openssl1.1-compat +3. **Validate config**: + - Next.js: `output: 'standalone'` in next.config.js + - SPAs: nginx.conf exists or needs generation +4. **Set build-time env vars** (DATABASE_URL for Prisma) +5. **Expose correct port** based on framework defaults +6. **Generate .dockerignore** if missing + +## Usage by Agents + +When creating a Dockerfile: + +1. Read package.json to detect framework +2. Check for prisma directory or @prisma/client dependency +3. Select appropriate template from this reference +4. Validate framework-specific requirements +5. Generate Dockerfile and supporting files (nginx.conf, .dockerignore) diff --git a/.claude/references/eureka-infrastructure.md b/.claude/references/eureka-infrastructure.md new file mode 100644 index 0000000..b459d17 --- /dev/null +++ b/.claude/references/eureka-infrastructure.md @@ -0,0 +1,237 @@ +# Eureka Platform Infrastructure Reference + +Reference documentation for services available on the Eureka deployment platform. + +## Injected Environment Variables + +These variables are **automatically injected** by Eureka platform at runtime: + +```bash +# Database +DATABASE_URL + +# MinIO +MINIO_ENDPOINT +MINIO_PORT +MINIO_ROOT_USER +MINIO_ROOT_PASSWORD +MINIO_BUCKET_NAME +MINIO_USE_SSL +MINIO_PUBLIC_URL +``` + +## Available Services + +### PostgreSQL Database + +| Setting | Value | +|----------------|-----------------------------------------------------------| +| Host | `postgres` (Docker network) | +| Port | `5432` | +| Database | `eurekalabo` | +| Username | `appuser` | +| Password | `supersecret` | +| Connection URL | `postgresql://appuser:supersecret@postgres:5432/eurekalabo` | + +**Environment Variable (injected):** +```bash +DATABASE_URL="postgresql://appuser:supersecret@postgres:5432/eurekalabo" +``` + +### MinIO Object Storage (S3-Compatible) + +| Setting | Value | +|---------------|------------------------------------| +| Endpoint | `minio` (Docker network) | +| Port | `9000` | +| Root User | `minioadmin` | +| Root Password | `minioadmin` | +| Bucket Name | `eurekalabo-files` | +| Use SSL | `false` | +| Public URL | `https://minio.162-43-92-100.nip.io` | + +**Environment Variables (injected):** +```bash +MINIO_ENDPOINT="minio" +MINIO_PORT="9000" +MINIO_ROOT_USER="minioadmin" +MINIO_ROOT_PASSWORD="minioadmin" +MINIO_BUCKET_NAME="eurekalabo-files" +MINIO_USE_SSL="false" +MINIO_PUBLIC_URL="https://minio.162-43-92-100.nip.io" +``` + +--- + +## Docker Network Architecture + +Services run on shared Docker networks: +- `base_data` - Database connections +- `base_web` - Web/HTTP traffic +- `shared_backend` - Internal service communication + +Your application container automatically joins these networks when deployed. + +--- + +## Framework Integration + +### Prisma (Node.js) + +**prisma/schema.prisma:** +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +**No changes needed** - Prisma reads `DATABASE_URL` automatically. + +### Drizzle (Node.js) + +**drizzle.config.ts:** +```typescript +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + schema: './src/db/schema.ts', + out: './drizzle', + dialect: 'postgresql', + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); +``` + +### SQLAlchemy (Python) + +```python +from sqlalchemy import create_engine +import os + +engine = create_engine(os.environ['DATABASE_URL']) +``` + +### MinIO / S3 Client (Node.js) + +**Using AWS SDK v3:** +```typescript +import { S3Client } from '@aws-sdk/client-s3'; + +const s3 = new S3Client({ + endpoint: `http://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}`, + region: 'us-east-1', + credentials: { + accessKeyId: process.env.MINIO_ROOT_USER!, + secretAccessKey: process.env.MINIO_ROOT_PASSWORD!, + }, + forcePathStyle: true, // Required for MinIO +}); + +// Use bucket: process.env.MINIO_BUCKET_NAME +``` + +**Using MinIO SDK:** +```typescript +import { Client } from 'minio'; + +const minio = new Client({ + endPoint: process.env.MINIO_ENDPOINT!, + port: parseInt(process.env.MINIO_PORT || '9000'), + useSSL: process.env.MINIO_USE_SSL === 'true', + accessKey: process.env.MINIO_ROOT_USER!, + secretKey: process.env.MINIO_ROOT_PASSWORD!, +}); + +// Use bucket: process.env.MINIO_BUCKET_NAME +``` + +### MinIO / S3 Client (Python) + +**Using boto3:** +```python +import boto3 +import os + +s3 = boto3.client( + 's3', + endpoint_url=f"http://{os.environ['MINIO_ENDPOINT']}:{os.environ['MINIO_PORT']}", + aws_access_key_id=os.environ['MINIO_ROOT_USER'], + aws_secret_access_key=os.environ['MINIO_ROOT_PASSWORD'], +) + +# Use bucket: os.environ['MINIO_BUCKET_NAME'] +``` + +--- + +## Dockerfile Environment Variables + +When building Docker images, these environment variables are injected at runtime, NOT build time. + +**DO NOT hardcode credentials in Dockerfile.** + +**Correct pattern:** +```dockerfile +# Runtime env vars - set by Eureka platform +ENV NODE_ENV=production +# DATABASE_URL, MINIO_* injected at runtime +``` + +**Wrong pattern:** +```dockerfile +# NEVER DO THIS +ENV DATABASE_URL="postgresql://..." +ENV MINIO_ROOT_PASSWORD="..." +``` + +--- + +## Pre-Deployment Checklist + +Before deploying, verify your app: + +1. **Reads from environment variables** (not hardcoded) +2. **Has `.env.example`** documenting required vars +3. **Dockerfile doesn't contain secrets** +4. **Database migrations** are ready (`prisma migrate deploy`, etc.) + +### Migration Commands + +**Prisma:** +```bash +npx prisma migrate deploy +``` + +**Drizzle:** +```bash +npx drizzle-kit migrate +``` + +**Django:** +```bash +python manage.py migrate +``` + +--- + +## Troubleshooting + +### Database Connection Failed + +1. Check container is on correct networks +2. Verify `DATABASE_URL` format +3. Check postgres container is running: `docker ps | grep postgres` + +### MinIO Upload Failed + +1. Verify bucket exists: `eurekalabo-files` +2. Check `forcePathStyle: true` for AWS SDK +3. Verify endpoint format: `http://minio:9000` (not `https`) + +### Environment Variables Not Available + +1. Check `.env` file exists (copy from `.env.example`) +2. Verify Eureka deployment injects vars +3. Check Docker Compose env_file configuration diff --git a/.claude/references/stripe-config.example.yml b/.claude/references/stripe-config.example.yml new file mode 100644 index 0000000..543161a --- /dev/null +++ b/.claude/references/stripe-config.example.yml @@ -0,0 +1,303 @@ +# Stripe Payment Configuration +# Copy this file to your project root as `stripe-config.yml` +# The stripe-payment-implementer agent will read this for context + +version: "1.0" + +# Project payment configuration +project: + name: "My Project" + stripe_account_type: "standard" # standard, express, custom (for Connect) + +# ============================================================================= +# ENABLED PAYMENT TYPES +# ============================================================================= +# Uncomment and configure the payment types your project uses + +payment_types: + + # --------------------------------------------------------------------------- + # One-Time Payments + # --------------------------------------------------------------------------- + one_time_payment: + enabled: true + implementation: "checkout" # checkout, payment_intent, payment_link + products: + - id: "prod_basic" + name: "Basic Package" + price_cents: 2999 + currency: "usd" + - id: "prod_premium" + name: "Premium Package" + price_cents: 9999 + currency: "usd" + + # --------------------------------------------------------------------------- + # Subscriptions + # --------------------------------------------------------------------------- + subscription: + enabled: true + implementation: "checkout" # checkout, subscription_api + trial_days: 14 + plans: + - id: "plan_starter" + name: "Starter" + price_id: "price_xxxxx" # From Stripe Dashboard + interval: "month" + price_cents: 999 + features: + - "5 projects" + - "Basic support" + - "1GB storage" + + - id: "plan_pro" + name: "Pro" + price_id: "price_yyyyy" + interval: "month" + price_cents: 2999 + features: + - "Unlimited projects" + - "Priority support" + - "10GB storage" + - "API access" + + - id: "plan_enterprise" + name: "Enterprise" + price_id: "price_zzzzz" + interval: "year" + price_cents: 99900 + features: + - "Everything in Pro" + - "Dedicated support" + - "Unlimited storage" + - "Custom integrations" + - "SLA guarantee" + + # --------------------------------------------------------------------------- + # Metered/Usage-Based Billing + # --------------------------------------------------------------------------- + metered_billing: + enabled: false + metrics: + - id: "api_calls" + name: "API Calls" + price_id: "price_metered_api" + unit_label: "calls" + aggregate_usage: "sum" + tiers: + - up_to: 1000 + unit_amount_cents: 0 # Free tier + - up_to: 10000 + unit_amount_cents: 1 # $0.01 per call + - up_to: "inf" + unit_amount_cents: 0.5 # $0.005 per call + + # --------------------------------------------------------------------------- + # Marketplace (Stripe Connect) + # --------------------------------------------------------------------------- + marketplace: + enabled: false + account_type: "express" # standard, express, custom + platform_fee: + type: "percentage" # percentage, fixed, both + percentage: 10 # 10% platform fee + fixed_cents: 0 # Additional fixed fee + payout_schedule: "automatic" # automatic, manual + + # --------------------------------------------------------------------------- + # Invoicing + # --------------------------------------------------------------------------- + invoicing: + enabled: false + default_due_days: 30 + collection_method: "send_invoice" # charge_automatically, send_invoice + auto_advance: true + footer: "Thank you for your business!" + + # --------------------------------------------------------------------------- + # Payment Links + # --------------------------------------------------------------------------- + payment_links: + enabled: false + products: [] # Reference product IDs from one_time_payment or subscription + + # --------------------------------------------------------------------------- + # Customer Portal + # --------------------------------------------------------------------------- + customer_portal: + enabled: true + features: + update_payment_method: true + cancel_subscription: true + switch_plans: true + view_invoices: true + update_billing_address: true + +# ============================================================================= +# PAYMENT METHODS +# ============================================================================= +# Which payment methods to accept + +payment_methods: + # Cards (always enabled) + cards: + enabled: true + brands: + - visa + - mastercard + - amex + - discover + + # Digital Wallets + wallets: + apple_pay: true + google_pay: true + link: true # Stripe's 1-click checkout + + # Bank Payments + bank: + us_bank_account: false # ACH + sepa_debit: false + + # Buy Now Pay Later + bnpl: + afterpay_clearpay: false + klarna: false + affirm: false + + # Regional Methods (uncomment as needed) + regional: + ideal: false # Netherlands + bancontact: false # Belgium + giropay: false # Germany + sofort: false # EU + boleto: false # Brazil + oxxo: false # Mexico + konbini: false # Japan + +# ============================================================================= +# WEBHOOK CONFIGURATION +# ============================================================================= +# Which webhook events to handle + +webhooks: + endpoint: "/api/webhooks/stripe" + + # Payment events + payment_events: + - "payment_intent.succeeded" + - "payment_intent.payment_failed" + - "charge.refunded" + - "charge.dispute.created" + + # Checkout events + checkout_events: + - "checkout.session.completed" + - "checkout.session.expired" + + # Subscription events (if subscriptions enabled) + subscription_events: + - "customer.subscription.created" + - "customer.subscription.updated" + - "customer.subscription.deleted" + - "customer.subscription.trial_will_end" + - "invoice.paid" + - "invoice.payment_failed" + - "invoice.upcoming" + + # Connect events (if marketplace enabled) + connect_events: + - "account.updated" + - "account.application.authorized" + - "account.application.deauthorized" + - "payout.paid" + - "payout.failed" + +# ============================================================================= +# DATABASE MAPPING +# ============================================================================= +# How Stripe objects map to your database + +database: + # User-Customer mapping + user_table: "users" + customer_id_field: "stripe_customer_id" + + # Subscription tracking + subscription_table: "subscriptions" + subscription_fields: + stripe_subscription_id: "stripe_subscription_id" + stripe_price_id: "stripe_price_id" + status: "status" + current_period_start: "current_period_start" + current_period_end: "current_period_end" + + # Order/Purchase tracking (for one-time payments) + order_table: "orders" + order_fields: + stripe_payment_intent_id: "stripe_payment_intent_id" + stripe_checkout_session_id: "stripe_checkout_session_id" + amount: "amount" + status: "status" + +# ============================================================================= +# ENVIRONMENT VARIABLES +# ============================================================================= +# Reference for required environment variables + +environment: + required: + - STRIPE_SECRET_KEY + - STRIPE_PUBLISHABLE_KEY + - STRIPE_WEBHOOK_SECRET + + optional: + - STRIPE_PRICE_ID_STARTER + - STRIPE_PRICE_ID_PRO + - STRIPE_PRICE_ID_ENTERPRISE + +# ============================================================================= +# API ROUTES TO GENERATE +# ============================================================================= +# Routes that should be created based on enabled features + +routes: + # Checkout + - path: "/api/checkout" + method: "POST" + purpose: "Create checkout session" + requires: ["one_time_payment", "subscription"] + + # Webhooks + - path: "/api/webhooks/stripe" + method: "POST" + purpose: "Handle Stripe webhooks" + requires: ["webhooks"] + + # Customer Portal + - path: "/api/billing/portal" + method: "POST" + purpose: "Create customer portal session" + requires: ["customer_portal"] + + # Subscription Management + - path: "/api/subscription" + method: "GET" + purpose: "Get current subscription" + requires: ["subscription"] + + - path: "/api/subscription/cancel" + method: "POST" + purpose: "Cancel subscription" + requires: ["subscription"] + + # Connect (Marketplace) + - path: "/api/connect/onboard" + method: "POST" + purpose: "Create Connect account link" + requires: ["marketplace"] + + - path: "/api/connect/dashboard" + method: "GET" + purpose: "Get Connect dashboard link" + requires: ["marketplace"] diff --git a/.claude/references/stripe-payment-types.md b/.claude/references/stripe-payment-types.md new file mode 100644 index 0000000..b07e1ef --- /dev/null +++ b/.claude/references/stripe-payment-types.md @@ -0,0 +1,427 @@ +# Stripe Payment Types Reference + +Quick reference for all Stripe payment types and their configurations. + +--- + +## Payment Types Catalog + +### ONE_TIME_PAYMENT + +**ID**: `one_time_payment` + +**Description**: Single, non-recurring payments for products or services. + +**Use Cases**: +- E-commerce purchases +- One-time donations +- Service fees +- Digital product sales + +**Stripe Objects**: +- `PaymentIntent` or `Checkout Session (mode: 'payment')` + +**Required Fields**: +```yaml +amount: integer # Amount in cents +currency: string # ISO currency code (usd, eur, jpy) +description: string # Payment description +``` + +**Documentation**: https://docs.stripe.com/payments/accept-a-payment + +--- + +### SUBSCRIPTION + +**ID**: `subscription` + +**Description**: Recurring payments at fixed intervals. + +**Use Cases**: +- SaaS subscriptions +- Membership sites +- Newsletter subscriptions +- Recurring donations + +**Stripe Objects**: +- `Product`, `Price`, `Subscription` +- `Checkout Session (mode: 'subscription')` + +**Required Fields**: +```yaml +price_id: string # Stripe Price ID +interval: enum # day, week, month, year +interval_count: integer # Every N intervals +trial_days: integer # Optional trial period +``` + +**Billing States**: +| State | Description | +|-------|-------------| +| `trialing` | In free trial period | +| `active` | Paid and active | +| `past_due` | Payment failed, in retry | +| `canceled` | Subscription ended | +| `unpaid` | All retries exhausted | +| `paused` | Temporarily paused | + +**Documentation**: https://docs.stripe.com/billing/subscriptions/overview + +--- + +### METERED_BILLING + +**ID**: `metered_billing` + +**Description**: Usage-based billing calculated at end of period. + +**Use Cases**: +- API call billing +- Storage usage +- Compute time +- Message/SMS charges + +**Stripe Objects**: +- `Price (usage_type: 'metered')` +- `UsageRecord` + +**Required Fields**: +```yaml +price_id: string # Metered price ID +aggregate_usage: enum # sum, last_during_period, last_ever, max +billing_scheme: per_unit # Per unit pricing +``` + +**Usage Reporting**: +```typescript +await stripe.subscriptionItems.createUsageRecord( + subscriptionItemId, + { quantity: 100, timestamp: 'now' } +); +``` + +**Documentation**: https://docs.stripe.com/billing/subscriptions/usage-based + +--- + +### TIERED_PRICING + +**ID**: `tiered_pricing` + +**Description**: Volume-based pricing with different rates at quantity thresholds. + +**Use Cases**: +- Bulk discounts +- Volume licensing +- Enterprise pricing tiers + +**Stripe Objects**: +- `Price (billing_scheme: 'tiered')` + +**Tier Modes**: +| Mode | Description | +|------|-------------| +| `volume` | All units at highest tier reached | +| `graduated` | Each tier priced separately | + +**Required Fields**: +```yaml +tiers: + - up_to: 10 + unit_amount: 1000 # $10 per unit + - up_to: 100 + unit_amount: 800 # $8 per unit + - up_to: inf + unit_amount: 500 # $5 per unit +tiers_mode: volume|graduated +``` + +**Documentation**: https://docs.stripe.com/billing/subscriptions/tiers + +--- + +### MARKETPLACE_PAYMENT + +**ID**: `marketplace_payment` + +**Description**: Multi-party payments with platform fees (Stripe Connect). + +**Use Cases**: +- Multi-vendor marketplaces +- Service platforms (Uber-like) +- Crowdfunding +- Affiliate payments + +**Account Types**: +| Type | Control | Onboarding | Dashboard | +|------|---------|------------|-----------| +| `standard` | Low | Full Stripe | Full | +| `express` | Medium | Simplified | Limited | +| `custom` | Full | Your UI | None | + +**Required Fields**: +```yaml +account_type: enum # standard, express, custom +application_fee_percent: float # Platform fee % +# OR +application_fee_amount: integer # Fixed fee in cents +``` + +**Payment Flow**: +```typescript +// Direct charge with fee +await stripe.paymentIntents.create({ + amount: 10000, + currency: 'usd', + application_fee_amount: 1000, // $10 platform fee + transfer_data: { + destination: 'acct_connected_account', + }, +}); +``` + +**Documentation**: https://docs.stripe.com/connect + +--- + +### PAYMENT_LINK + +**ID**: `payment_link` + +**Description**: No-code shareable payment URLs. + +**Use Cases**: +- Quick sales via email/SMS +- Social media selling +- Invoice payments +- Simple checkout without website + +**Stripe Objects**: +- `PaymentLink` + +**Required Fields**: +```yaml +price_id: string # Product price +quantity_adjustable: boolean +allow_promotion_codes: boolean +``` + +**Documentation**: https://docs.stripe.com/payment-links + +--- + +### INVOICE + +**ID**: `invoice` + +**Description**: Send invoices for payment (B2B, services). + +**Use Cases**: +- B2B billing +- Professional services +- Custom quoted work +- Net-30/60/90 terms + +**Stripe Objects**: +- `Invoice`, `InvoiceItem` + +**Required Fields**: +```yaml +customer_id: string +due_days: integer # Days until due +collection_method: enum # charge_automatically, send_invoice +``` + +**Documentation**: https://docs.stripe.com/invoicing + +--- + +### SAVED_PAYMENT_METHOD + +**ID**: `saved_payment_method` + +**Description**: Store payment methods for future use. + +**Use Cases**: +- One-click checkout +- Subscription updates +- Recurring manual charges + +**Stripe Objects**: +- `SetupIntent`, `PaymentMethod`, `Customer` + +**Required Fields**: +```yaml +customer_id: string +payment_method_types: array # ['card', 'us_bank_account'] +usage: enum # on_session, off_session +``` + +**Documentation**: https://docs.stripe.com/payments/save-and-reuse + +--- + +### CUSTOMER_PORTAL + +**ID**: `customer_portal` + +**Description**: Self-service subscription management. + +**Use Cases**: +- Subscription upgrades/downgrades +- Payment method updates +- Invoice history +- Cancellation flow + +**Stripe Objects**: +- `BillingPortal.Session`, `BillingPortal.Configuration` + +**Portal Features**: +- Update payment method +- View invoices +- Cancel subscription +- Change plan + +**Documentation**: https://docs.stripe.com/customer-management/portal-deep-dive + +--- + +## Payment Methods Catalog + +### Cards + +| Method | ID | Regions | +|--------|-----|---------| +| Visa/Mastercard/Amex | `card` | Global | +| Apple Pay | `apple_pay` | Global (iOS/Safari) | +| Google Pay | `google_pay` | Global (Android/Chrome) | +| Link | `link` | US | + +### Bank Payments + +| Method | ID | Regions | +|--------|-----|---------| +| ACH Direct Debit | `us_bank_account` | US | +| SEPA Direct Debit | `sepa_debit` | EU | +| BACS Direct Debit | `bacs_debit` | UK | +| Pre-authorized Debit | `acss_debit` | Canada | + +### Regional Methods + +| Method | ID | Regions | +|--------|-----|---------| +| iDEAL | `ideal` | Netherlands | +| Bancontact | `bancontact` | Belgium | +| giropay | `giropay` | Germany | +| Sofort | `sofort` | EU | +| Przelewy24 | `p24` | Poland | +| EPS | `eps` | Austria | +| Boleto | `boleto` | Brazil | +| OXXO | `oxxo` | Mexico | +| Konbini | `konbini` | Japan | +| PayNow | `paynow` | Singapore | +| PromptPay | `promptpay` | Thailand | +| FPX | `fpx` | Malaysia | + +### Buy Now, Pay Later + +| Method | ID | Regions | +|--------|-----|---------| +| Afterpay/Clearpay | `afterpay_clearpay` | US, UK, AU, NZ, CA | +| Klarna | `klarna` | US, EU, UK | +| Affirm | `affirm` | US, CA | +| Zip | `zip` | US, AU | + +### Wallets + +| Method | ID | Regions | +|--------|-----|---------| +| Alipay | `alipay` | China | +| WeChat Pay | `wechat_pay` | China | +| GrabPay | `grabpay` | Singapore, Malaysia | +| PayPal | `paypal` | Global | + +--- + +## Webhook Events Reference + +### Payment Events + +| Event | When | Action | +|-------|------|--------| +| `payment_intent.succeeded` | Payment successful | Fulfill order | +| `payment_intent.payment_failed` | Payment failed | Notify customer | +| `charge.refunded` | Refund processed | Update order status | +| `charge.dispute.created` | Chargeback initiated | Review dispute | + +### Checkout Events + +| Event | When | Action | +|-------|------|--------| +| `checkout.session.completed` | Checkout finished | Fulfill order | +| `checkout.session.expired` | Session expired | Clean up | +| `checkout.session.async_payment_succeeded` | Async payment success | Fulfill order | +| `checkout.session.async_payment_failed` | Async payment failed | Notify customer | + +### Subscription Events + +| Event | When | Action | +|-------|------|--------| +| `customer.subscription.created` | New subscription | Provision access | +| `customer.subscription.updated` | Plan changed | Update access level | +| `customer.subscription.deleted` | Subscription ended | Revoke access | +| `customer.subscription.trial_will_end` | Trial ending soon | Send reminder | +| `customer.subscription.paused` | Subscription paused | Pause access | +| `customer.subscription.resumed` | Subscription resumed | Resume access | + +### Invoice Events + +| Event | When | Action | +|-------|------|--------| +| `invoice.paid` | Invoice paid | Update records | +| `invoice.payment_failed` | Payment failed | Retry/notify | +| `invoice.upcoming` | Invoice coming | Notify customer | +| `invoice.finalized` | Invoice ready | Send to customer | + +### Connect Events + +| Event | When | Action | +|-------|------|--------| +| `account.updated` | Account changed | Check status | +| `account.application.authorized` | Connect authorized | Enable features | +| `account.application.deauthorized` | Connect removed | Disable features | +| `payout.paid` | Payout sent | Update records | +| `payout.failed` | Payout failed | Investigate | + +--- + +## Quick Decision Tree + +``` +What payment model do you need? + +├─ Single purchase? +│ ├─ Simple checkout → PAYMENT_LINK or ONE_TIME_PAYMENT (Checkout) +│ └─ Custom UI → ONE_TIME_PAYMENT (Payment Intent + Elements) +│ +├─ Recurring billing? +│ ├─ Fixed price → SUBSCRIPTION +│ ├─ Usage-based → METERED_BILLING +│ └─ Volume discounts → TIERED_PRICING +│ +├─ Multi-party payments? +│ └─ Platform fees → MARKETPLACE_PAYMENT (Connect) +│ +├─ B2B invoicing? +│ └─ Custom terms → INVOICE +│ +└─ Customer self-service? + └─ Manage subscriptions → CUSTOMER_PORTAL +``` + +--- + +## Version History + +- v1.0.0 - Initial payment types catalog diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..8ab61e4 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,225 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_bash.py\" --command \"$TOOL_INPUT_COMMAND\"" + } + ] + }, + { + "matcher": "Task", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation task --input \"$TOOL_INPUT\"" + } + ] + }, + { + "matcher": "Write", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation write --file \"$TOOL_INPUT_FILE_PATH\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "Edit", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --file \"$TOOL_INPUT_FILE_PATH\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "MultiEdit", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --file \"$TOOL_INPUT_FILE_PATH\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "NotebookEdit", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --file \"$TOOL_INPUT_NOTEBOOK_PATH\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_NOTEBOOK_PATH\"" + } + ] + }, + { + "matcher": "mcp__serena__create_text_file", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation write --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "mcp__serena__replace_content", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "mcp__serena__replace_symbol_body", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "mcp__morphllm-fast-apply__write_file", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation write --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "mcp__morphllm-fast-apply__tiny_edit_file", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "mcp__filesystem__write_file", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation write --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_PATH\"" + } + ] + }, + { + "matcher": "mcp__filesystem__edit_file", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation edit --input \"$TOOL_INPUT\"" + }, + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_PATH\"" + } + ] + }, + { + "matcher": "mcp__filesystem__create_directory", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation write --input \"$TOOL_INPUT\"" + } + ] + }, + { + "matcher": "mcp__filesystem__move_file", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/validate_workflow.py\" --operation write --input \"$TOOL_INPUT\"" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Write", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/post_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "Edit", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/post_write.py\" --manifest \"$CLAUDE_PROJECT_DIR/project_manifest.json\" --file \"$TOOL_INPUT_FILE_PATH\"" + } + ] + }, + { + "matcher": "Task", + "hooks": [ + { + "type": "command", + "command": "echo '🔄 Agent task completed. Verify outputs before proceeding.'" + } + ] + } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 \"$CLAUDE_PROJECT_DIR/skills/guardrail-orchestrator/scripts/workflow_manager.py\" status 2>/dev/null || echo '🛡️ Session complete (no active workflow)'" + } + ] + } + ] + } +} diff --git a/.gitignore b/.gitignore index 5ef6a52..9a6a064 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,8 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# Eureka Factory credentials +.claude/eureka-factory.yaml +.claude/eureka-factory.yml +.claude/eureka-factory.json diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..be99453 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "eureka-docs": { + "command": "npx", + "args": ["eureka-docs-server"], + "env": {} + }, + "eureka-imagen": { + "command": "npx", + "args": ["eureka-imagen-server"], + "env": { + "IMAGEROUTER_API_KEY": "${IMAGEROUTER_API_KEY}" + } + } + } +} diff --git a/.workflow/gathered_context.yml b/.workflow/gathered_context.yml new file mode 100644 index 0000000..809422d --- /dev/null +++ b/.workflow/gathered_context.yml @@ -0,0 +1,28 @@ +# Gathered Project Context +# Generated: 2024-12-19 + +existing_models: [] + +existing_apis: [] + +existing_pages: + - route: / + file: app/page.tsx + description: Default Next.js home page + +existing_components: [] + +dependency_chains: [] + +detected_patterns: + naming: camelCase for functions, PascalCase for components + file_structure: Next.js App Router (app directory) + imports: "@/" alias expected + +tech_stack: + next_version: "16.1.0" + react_version: "19.2.3" + tailwind_version: "4" + typescript: true + +reference_implementations: [] diff --git a/.workflow/index.yml b/.workflow/index.yml new file mode 100644 index 0000000..827dbe0 --- /dev/null +++ b/.workflow/index.yml @@ -0,0 +1,11 @@ +versions: +- version: v001 + feature: Voice recording app with AI summarization and automatic app generation + triggered by wakeup-word エウレカアプリ作って + status: completed + started_at: '2025-12-19T05:57:58.578500' + completed_at: '2025-12-19T06:18:09.905506' + tasks_count: 0 + operations_count: 0 +latest_version: v001 +total_versions: 1 diff --git a/.workflow/versions/v001/contexts/api_create_recording.yml b/.workflow/versions/v001/contexts/api_create_recording.yml new file mode 100644 index 0000000..6bda597 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_create_recording.yml @@ -0,0 +1,160 @@ +task_id: task_create_api_create_recording +entity_id: api_create_recording +generated_at: '2025-12-19T06:08:12.151391' +workflow_version: v001 +target: + type: api + definition: + id: api_create_recording + method: POST + path: /api/recordings + summary: Create new recording + description: Upload audio file and create recording metadata + tags: + - recordings + path_params: [] + query_params: [] + request_body: + content_type: multipart/form-data + schema: + type: object + properties: + - name: audio + type: file + required: true + description: Audio file (webm, mp3, wav) + - name: title + type: string + required: false + description: Recording title (auto-generated if not provided) + - name: duration + type: integer + required: true + description: Duration in seconds + responses: + - status: 201 + description: Recording created + schema: + type: object + properties: + - name: id + type: uuid + - name: title + type: string + - name: audio_file_path + type: string + - name: duration + type: integer + - name: created_at + type: datetime + example: + id: rec-123 + title: Recording 2024-12-19 + audio_file_path: recordings/user-123/rec-123.webm + duration: 180 + created_at: '2024-12-19T06:00:00Z' + - status: 400 + description: Invalid audio file + schema: + type: object + properties: + - name: error + type: string + example: + error: Invalid audio format + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_recording + definition: &id001 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_recording + definitions: + - id: model_recording + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/recordings/route.ts + reference: [] +acceptance: +- criterion: POST /api/recordings returns success response + verification: curl -X POST /api/recordings +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_delete_app.yml b/.workflow/versions/v001/contexts/api_delete_app.yml new file mode 100644 index 0000000..5f1ae1e --- /dev/null +++ b/.workflow/versions/v001/contexts/api_delete_app.yml @@ -0,0 +1,158 @@ +task_id: task_create_api_delete_app +entity_id: api_delete_app +generated_at: '2025-12-19T06:08:12.168925' +workflow_version: v001 +target: + type: api + definition: + id: api_delete_app + method: DELETE + path: /api/apps/[id] + summary: Delete generated app + description: Delete generated app (recording remains) + tags: + - apps + path_params: + - name: id + type: uuid + description: App ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: App deleted + schema: + type: object + properties: + - name: success + type: boolean + example: + success: true + - status: 404 + description: App not found + schema: + type: object + properties: + - name: error + type: string + example: + error: App not found + depends_on_models: + - model_generated_app + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_generated_app + definition: &id001 + id: model_generated_app + name: GeneratedApp + description: AI-generated application from recording content + table_name: generated_apps + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique app identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this app + - name: recording_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Source recording that triggered generation + - name: title + type: string + constraints: + - not_null + description: App title from AI analysis + - name: description + type: text + constraints: [] + description: App description + - name: html_content + type: text + constraints: + - not_null + description: Complete HTML/CSS/JS for iframe rendering + - name: prd_content + type: text + constraints: [] + description: Product Requirements Document (PRD) generated by AI + - name: ui_ux_design + type: text + constraints: [] + description: UI/UX design notes from AI + - name: app_type + type: string + constraints: [] + description: Type of app determined by AI (e.g., todo, calculator, form) + - name: status + type: enum + enum_values: + - generating + - completed + - failed + constraints: + - not_null + - default + default: generating + description: Generation status + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + - type: belongs_to + target: model_recording + foreign_key: recording_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_apps_user_created + - fields: + - recording_id + unique: false + name: idx_apps_recording + timestamps: true + soft_delete: false + validations: [] + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_generated_app + definitions: + - id: model_generated_app + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/apps/[id]/route.ts + reference: [] +acceptance: +- criterion: DELETE /api/apps/[id] returns success response + verification: curl -X DELETE /api/apps/[id] +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_delete_recording.yml b/.workflow/versions/v001/contexts/api_delete_recording.yml new file mode 100644 index 0000000..22091f5 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_delete_recording.yml @@ -0,0 +1,139 @@ +task_id: task_create_api_delete_recording +entity_id: api_delete_recording +generated_at: '2025-12-19T06:08:12.155224' +workflow_version: v001 +target: + type: api + definition: + id: api_delete_recording + method: DELETE + path: /api/recordings/[id] + summary: Delete recording + description: Delete recording and associated audio file + tags: + - recordings + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Recording deleted + schema: + type: object + properties: + - name: success + type: boolean + example: + success: true + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_recording + definition: &id001 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_recording + definitions: + - id: model_recording + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/recordings/[id]/route.ts + reference: [] +acceptance: +- criterion: DELETE /api/recordings/[id] returns success response + verification: curl -X DELETE /api/recordings/[id] +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_generate_app.yml b/.workflow/versions/v001/contexts/api_generate_app.yml new file mode 100644 index 0000000..c62ee26 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_generate_app.yml @@ -0,0 +1,315 @@ +task_id: task_create_api_generate_app +entity_id: api_generate_app +generated_at: '2025-12-19T06:08:12.163139' +workflow_version: v001 +target: + type: api + definition: + id: api_generate_app + method: POST + path: /api/apps/generate + summary: Generate app from recording + description: Trigger AI app generation based on recording summary (wake word detection) + tags: + - apps + - ai + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: recording_id + type: uuid + required: true + description: Source recording ID + example: + recording_id: rec-123 + responses: + - status: 201 + description: App generation started + schema: + type: object + properties: + - name: id + type: uuid + - name: recording_id + type: uuid + - name: status + type: string + example: + id: app-123 + recording_id: rec-123 + status: generating + - status: 400 + description: Recording has no summary + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording summary not available + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_generated_app + - model_recording + depends_on_apis: + - api_summarize_recording + auth: + required: true + roles: [] +related: + models: + - id: model_generated_app + definition: &id002 + id: model_generated_app + name: GeneratedApp + description: AI-generated application from recording content + table_name: generated_apps + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique app identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this app + - name: recording_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Source recording that triggered generation + - name: title + type: string + constraints: + - not_null + description: App title from AI analysis + - name: description + type: text + constraints: [] + description: App description + - name: html_content + type: text + constraints: + - not_null + description: Complete HTML/CSS/JS for iframe rendering + - name: prd_content + type: text + constraints: [] + description: Product Requirements Document (PRD) generated by AI + - name: ui_ux_design + type: text + constraints: [] + description: UI/UX design notes from AI + - name: app_type + type: string + constraints: [] + description: Type of app determined by AI (e.g., todo, calculator, form) + - name: status + type: enum + enum_values: + - generating + - completed + - failed + constraints: + - not_null + - default + default: generating + description: Generation status + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + - type: belongs_to + target: model_recording + foreign_key: recording_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_apps_user_created + - fields: + - recording_id + unique: false + name: idx_apps_recording + timestamps: true + soft_delete: false + validations: [] + external: false + - id: model_recording + definition: &id003 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: + - id: api_summarize_recording + definition: &id001 + id: api_summarize_recording + method: POST + path: /api/recordings/[id]/summarize + summary: Summarize recording + description: Use Gemini to generate summary from transcript + tags: + - recordings + - ai + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Summary generated + schema: + type: object + properties: + - name: recording_id + type: uuid + - name: summary + type: string + example: + recording_id: rec-123 + summary: Meeting discussion about Q1 goals... + - status: 400 + description: No transcript available + schema: + type: object + properties: + - name: error + type: string + example: + error: Transcript not available + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: + - api_transcribe_recording + auth: + required: true + roles: [] + external: false + components: [] +dependencies: + entity_ids: + - api_summarize_recording + - model_generated_app + - model_recording + definitions: + - id: api_summarize_recording + type: api + definition: *id001 + external: false + - id: model_generated_app + type: model + definition: *id002 + external: false + - id: model_recording + type: model + definition: *id003 + external: false +files: + to_create: + - app/api/apps/generate/route.ts + reference: [] +acceptance: +- criterion: POST /api/apps/generate returns success response + verification: curl -X POST /api/apps/generate +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_get_app.yml b/.workflow/versions/v001/contexts/api_get_app.yml new file mode 100644 index 0000000..3b200ab --- /dev/null +++ b/.workflow/versions/v001/contexts/api_get_app.yml @@ -0,0 +1,170 @@ +task_id: task_create_api_get_app +entity_id: api_get_app +generated_at: '2025-12-19T06:08:12.166887' +workflow_version: v001 +target: + type: api + definition: + id: api_get_app + method: GET + path: /api/apps/[id] + summary: Get single generated app + description: Get app details including HTML content for iframe rendering + tags: + - apps + path_params: + - name: id + type: uuid + description: App ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: App details + schema: + type: object + properties: + - name: id + type: uuid + - name: recording_id + type: uuid + - name: title + type: string + - name: description + type: string + - name: html_content + type: string + - name: app_type + type: string + - name: status + type: string + - name: created_at + type: datetime + - status: 404 + description: App not found + schema: + type: object + properties: + - name: error + type: string + example: + error: App not found + depends_on_models: + - model_generated_app + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_generated_app + definition: &id001 + id: model_generated_app + name: GeneratedApp + description: AI-generated application from recording content + table_name: generated_apps + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique app identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this app + - name: recording_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Source recording that triggered generation + - name: title + type: string + constraints: + - not_null + description: App title from AI analysis + - name: description + type: text + constraints: [] + description: App description + - name: html_content + type: text + constraints: + - not_null + description: Complete HTML/CSS/JS for iframe rendering + - name: prd_content + type: text + constraints: [] + description: Product Requirements Document (PRD) generated by AI + - name: ui_ux_design + type: text + constraints: [] + description: UI/UX design notes from AI + - name: app_type + type: string + constraints: [] + description: Type of app determined by AI (e.g., todo, calculator, form) + - name: status + type: enum + enum_values: + - generating + - completed + - failed + constraints: + - not_null + - default + default: generating + description: Generation status + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + - type: belongs_to + target: model_recording + foreign_key: recording_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_apps_user_created + - fields: + - recording_id + unique: false + name: idx_apps_recording + timestamps: true + soft_delete: false + validations: [] + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_generated_app + definitions: + - id: model_generated_app + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/apps/[id]/route.ts + reference: [] +acceptance: +- criterion: GET /api/apps/[id] returns success response + verification: curl -X GET /api/apps/[id] +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_get_current_user.yml b/.workflow/versions/v001/contexts/api_get_current_user.yml new file mode 100644 index 0000000..acb6e76 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_get_current_user.yml @@ -0,0 +1,126 @@ +task_id: task_create_api_get_current_user +entity_id: api_get_current_user +generated_at: '2025-12-19T06:08:12.148074' +workflow_version: v001 +target: + type: api + definition: + id: api_get_current_user + method: GET + path: /api/auth/me + summary: Get current user + description: Get currently authenticated user information + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: User data + schema: + type: object + properties: + - name: id + type: uuid + - name: email + type: string + - name: name + type: string + - name: created_at + type: datetime + example: + id: 550e8400-e29b-41d4-a716-446655440000 + email: user@example.com + name: John Doe + created_at: '2024-12-19T06:00:00Z' + - status: 401 + description: Not authenticated + schema: + type: object + properties: + - name: error + type: string + example: + error: Not authenticated + depends_on_models: + - model_user + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_user + definition: &id001 + id: model_user + name: User + description: Application user account with email/password authentication + table_name: users + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique user identifier + - name: email + type: string + constraints: + - unique + - not_null + - indexed + description: User email address for login + - name: name + type: string + constraints: + - not_null + description: User's display name + - name: password_hash + type: string + constraints: + - not_null + description: Bcrypt hashed password + relations: [] + indexes: + - fields: + - email + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: Invalid email format + - field: name + rule: min:1 + message: Name is required + - field: password_hash + rule: min:60 + message: Invalid password hash + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_user + definitions: + - id: model_user + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/auth/me/route.ts + reference: [] +acceptance: +- criterion: GET /api/auth/me returns success response + verification: curl -X GET /api/auth/me +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_get_recording.yml b/.workflow/versions/v001/contexts/api_get_recording.yml new file mode 100644 index 0000000..83d3a9d --- /dev/null +++ b/.workflow/versions/v001/contexts/api_get_recording.yml @@ -0,0 +1,151 @@ +task_id: task_create_api_get_recording +entity_id: api_get_recording +generated_at: '2025-12-19T06:08:12.153374' +workflow_version: v001 +target: + type: api + definition: + id: api_get_recording + method: GET + path: /api/recordings/[id] + summary: Get single recording + description: Get recording details including transcript and summary + tags: + - recordings + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Recording details + schema: + type: object + properties: + - name: id + type: uuid + - name: title + type: string + - name: audio_file_path + type: string + - name: duration + type: integer + - name: transcript + type: string + - name: summary + type: string + - name: is_transcribing + type: boolean + - name: created_at + type: datetime + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_recording + definition: &id001 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_recording + definitions: + - id: model_recording + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/recordings/[id]/route.ts + reference: [] +acceptance: +- criterion: GET /api/recordings/[id] returns success response + verification: curl -X GET /api/recordings/[id] +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_list_apps.yml b/.workflow/versions/v001/contexts/api_list_apps.yml new file mode 100644 index 0000000..864132e --- /dev/null +++ b/.workflow/versions/v001/contexts/api_list_apps.yml @@ -0,0 +1,164 @@ +task_id: task_create_api_list_apps +entity_id: api_list_apps +generated_at: '2025-12-19T06:08:12.161202' +workflow_version: v001 +target: + type: api + definition: + id: api_list_apps + method: GET + path: /api/apps + summary: List generated apps + description: Get all apps generated for authenticated user + tags: + - apps + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: Maximum number of apps to return + - name: offset + type: integer + required: false + default: 0 + description: Number of apps to skip + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Apps list + schema: + type: object + properties: + - name: apps + type: array + - name: total + type: integer + example: + apps: + - id: app-123 + title: Todo App + app_type: todo + status: completed + created_at: '2024-12-19T06:00:00Z' + total: 12 + depends_on_models: + - model_generated_app + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_generated_app + definition: &id001 + id: model_generated_app + name: GeneratedApp + description: AI-generated application from recording content + table_name: generated_apps + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique app identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this app + - name: recording_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Source recording that triggered generation + - name: title + type: string + constraints: + - not_null + description: App title from AI analysis + - name: description + type: text + constraints: [] + description: App description + - name: html_content + type: text + constraints: + - not_null + description: Complete HTML/CSS/JS for iframe rendering + - name: prd_content + type: text + constraints: [] + description: Product Requirements Document (PRD) generated by AI + - name: ui_ux_design + type: text + constraints: [] + description: UI/UX design notes from AI + - name: app_type + type: string + constraints: [] + description: Type of app determined by AI (e.g., todo, calculator, form) + - name: status + type: enum + enum_values: + - generating + - completed + - failed + constraints: + - not_null + - default + default: generating + description: Generation status + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + - type: belongs_to + target: model_recording + foreign_key: recording_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_apps_user_created + - fields: + - recording_id + unique: false + name: idx_apps_recording + timestamps: true + soft_delete: false + validations: [] + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_generated_app + definitions: + - id: model_generated_app + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/apps/route.ts + reference: [] +acceptance: +- criterion: GET /api/apps returns success response + verification: curl -X GET /api/apps +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_list_recordings.yml b/.workflow/versions/v001/contexts/api_list_recordings.yml new file mode 100644 index 0000000..aae04a1 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_list_recordings.yml @@ -0,0 +1,144 @@ +task_id: task_create_api_list_recordings +entity_id: api_list_recordings +generated_at: '2025-12-19T06:08:12.149616' +workflow_version: v001 +target: + type: api + definition: + id: api_list_recordings + method: GET + path: /api/recordings + summary: List user recordings + description: Get all recordings for authenticated user, sorted by creation date + tags: + - recordings + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: Maximum number of recordings to return + - name: offset + type: integer + required: false + default: 0 + description: Number of recordings to skip + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Recordings list + schema: + type: object + properties: + - name: recordings + type: array + - name: total + type: integer + example: + recordings: + - id: rec-123 + title: Meeting Notes + duration: 180 + created_at: '2024-12-19T06:00:00Z' + total: 42 + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_recording + definition: &id001 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_recording + definitions: + - id: model_recording + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/recordings/route.ts + reference: [] +acceptance: +- criterion: GET /api/recordings returns success response + verification: curl -X GET /api/recordings +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_login_user.yml b/.workflow/versions/v001/contexts/api_login_user.yml new file mode 100644 index 0000000..4af8c0a --- /dev/null +++ b/.workflow/versions/v001/contexts/api_login_user.yml @@ -0,0 +1,139 @@ +task_id: task_create_api_login_user +entity_id: api_login_user +generated_at: '2025-12-19T06:08:12.145470' +workflow_version: v001 +target: + type: api + definition: + id: api_login_user + method: POST + path: /api/auth/login + summary: Login user + description: Authenticate user with email and password, return session token + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: email + type: string + required: true + validations: + - email + description: User email + - name: password + type: string + required: true + description: User password + example: + email: user@example.com + password: securepass123 + responses: + - status: 200 + description: Login successful + schema: + type: object + properties: + - name: user + type: object + - name: token + type: string + example: + user: + id: 550e8400-e29b-41d4-a716-446655440000 + email: user@example.com + name: John Doe + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + - status: 401 + description: Invalid credentials + schema: + type: object + properties: + - name: error + type: string + example: + error: Invalid email or password + depends_on_models: + - model_user + depends_on_apis: [] + auth: + required: false + roles: [] + rate_limit: + requests: 20 + window: 1h +related: + models: + - id: model_user + definition: &id001 + id: model_user + name: User + description: Application user account with email/password authentication + table_name: users + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique user identifier + - name: email + type: string + constraints: + - unique + - not_null + - indexed + description: User email address for login + - name: name + type: string + constraints: + - not_null + description: User's display name + - name: password_hash + type: string + constraints: + - not_null + description: Bcrypt hashed password + relations: [] + indexes: + - fields: + - email + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: Invalid email format + - field: name + rule: min:1 + message: Name is required + - field: password_hash + rule: min:60 + message: Invalid password hash + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_user + definitions: + - id: model_user + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/auth/login/route.ts + reference: [] +acceptance: +- criterion: POST /api/auth/login returns success response + verification: curl -X POST /api/auth/login +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_logout_user.yml b/.workflow/versions/v001/contexts/api_logout_user.yml new file mode 100644 index 0000000..9dc3094 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_logout_user.yml @@ -0,0 +1,54 @@ +task_id: task_create_api_logout_user +entity_id: api_logout_user +generated_at: '2025-12-19T06:08:12.147269' +workflow_version: v001 +target: + type: api + definition: + id: api_logout_user + method: POST + path: /api/auth/logout + summary: Logout user + description: Invalidate current session token + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Logout successful + schema: + type: object + properties: + - name: success + type: boolean + example: + success: true + depends_on_models: [] + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/api/auth/logout/route.ts + reference: [] +acceptance: +- criterion: POST /api/auth/logout returns success response + verification: curl -X POST /api/auth/logout +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_register_user.yml b/.workflow/versions/v001/contexts/api_register_user.yml new file mode 100644 index 0000000..10c479d --- /dev/null +++ b/.workflow/versions/v001/contexts/api_register_user.yml @@ -0,0 +1,161 @@ +task_id: task_create_api_register_user +entity_id: api_register_user +generated_at: '2025-12-19T06:08:12.143531' +workflow_version: v001 +target: + type: api + definition: + id: api_register_user + method: POST + path: /api/auth/register + summary: Register a new user + description: Create a new user account with email and password + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: email + type: string + required: true + validations: + - email + description: User email address + - name: name + type: string + required: true + validations: + - min:1 + - max:100 + description: User display name + - name: password + type: string + required: true + validations: + - min:8 + description: Password (min 8 characters) + example: + email: user@example.com + name: John Doe + password: securepass123 + responses: + - status: 201 + description: User created successfully + schema: + type: object + properties: + - name: id + type: uuid + - name: email + type: string + - name: name + type: string + - name: created_at + type: datetime + example: + id: 550e8400-e29b-41d4-a716-446655440000 + email: user@example.com + name: John Doe + created_at: '2024-12-19T06:00:00Z' + - status: 400 + description: Validation error + schema: + type: object + properties: + - name: error + type: string + example: + error: Invalid email or password too short + - status: 409 + description: Email already exists + schema: + type: object + properties: + - name: error + type: string + example: + error: Email already registered + depends_on_models: + - model_user + depends_on_apis: [] + auth: + required: false + roles: [] + rate_limit: + requests: 10 + window: 1h +related: + models: + - id: model_user + definition: &id001 + id: model_user + name: User + description: Application user account with email/password authentication + table_name: users + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique user identifier + - name: email + type: string + constraints: + - unique + - not_null + - indexed + description: User email address for login + - name: name + type: string + constraints: + - not_null + description: User's display name + - name: password_hash + type: string + constraints: + - not_null + description: Bcrypt hashed password + relations: [] + indexes: + - fields: + - email + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: Invalid email format + - field: name + rule: min:1 + message: Name is required + - field: password_hash + rule: min:60 + message: Invalid password hash + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_user + definitions: + - id: model_user + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/auth/register/route.ts + reference: [] +acceptance: +- criterion: POST /api/auth/register returns success response + verification: curl -X POST /api/auth/register +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_summarize_recording.yml b/.workflow/versions/v001/contexts/api_summarize_recording.yml new file mode 100644 index 0000000..11607b0 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_summarize_recording.yml @@ -0,0 +1,207 @@ +task_id: task_create_api_summarize_recording +entity_id: api_summarize_recording +generated_at: '2025-12-19T06:08:12.158694' +workflow_version: v001 +target: + type: api + definition: + id: api_summarize_recording + method: POST + path: /api/recordings/[id]/summarize + summary: Summarize recording + description: Use Gemini to generate summary from transcript + tags: + - recordings + - ai + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Summary generated + schema: + type: object + properties: + - name: recording_id + type: uuid + - name: summary + type: string + example: + recording_id: rec-123 + summary: Meeting discussion about Q1 goals... + - status: 400 + description: No transcript available + schema: + type: object + properties: + - name: error + type: string + example: + error: Transcript not available + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: + - api_transcribe_recording + auth: + required: true + roles: [] +related: + models: + - id: model_recording + definition: &id002 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: + - id: api_transcribe_recording + definition: &id001 + id: api_transcribe_recording + method: POST + path: /api/recordings/[id]/transcribe + summary: Transcribe recording + description: Run Whisper STT on audio file to generate transcript + tags: + - recordings + - ai + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Transcription started + schema: + type: object + properties: + - name: recording_id + type: uuid + - name: is_transcribing + type: boolean + example: + recording_id: rec-123 + is_transcribing: true + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + components: [] +dependencies: + entity_ids: + - api_transcribe_recording + - model_recording + definitions: + - id: api_transcribe_recording + type: api + definition: *id001 + external: false + - id: model_recording + type: model + definition: *id002 + external: false +files: + to_create: + - app/api/recordings/[id]/summarize/route.ts + reference: [] +acceptance: +- criterion: POST /api/recordings/[id]/summarize returns success response + verification: curl -X POST /api/recordings/[id]/summarize +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/api_transcribe_recording.yml b/.workflow/versions/v001/contexts/api_transcribe_recording.yml new file mode 100644 index 0000000..ec41b61 --- /dev/null +++ b/.workflow/versions/v001/contexts/api_transcribe_recording.yml @@ -0,0 +1,143 @@ +task_id: task_create_api_transcribe_recording +entity_id: api_transcribe_recording +generated_at: '2025-12-19T06:08:12.156931' +workflow_version: v001 +target: + type: api + definition: + id: api_transcribe_recording + method: POST + path: /api/recordings/[id]/transcribe + summary: Transcribe recording + description: Run Whisper STT on audio file to generate transcript + tags: + - recordings + - ai + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Transcription started + schema: + type: object + properties: + - name: recording_id + type: uuid + - name: is_transcribing + type: boolean + example: + recording_id: rec-123 + is_transcribing: true + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] +related: + models: + - id: model_recording + definition: &id001 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_recording + definitions: + - id: model_recording + type: model + definition: *id001 + external: false +files: + to_create: + - app/api/recordings/[id]/transcribe/route.ts + reference: [] +acceptance: +- criterion: POST /api/recordings/[id]/transcribe returns success response + verification: curl -X POST /api/recordings/[id]/transcribe +- criterion: Request validation implemented + verification: Test with invalid data +- criterion: Error responses match contract + verification: Test error scenarios diff --git a/.workflow/versions/v001/contexts/component_app_card.yml b/.workflow/versions/v001/contexts/component_app_card.yml new file mode 100644 index 0000000..7058fba --- /dev/null +++ b/.workflow/versions/v001/contexts/component_app_card.yml @@ -0,0 +1,51 @@ +task_id: task_create_component_app_card +entity_id: component_app_card +generated_at: '2025-12-19T06:08:12.199809' +workflow_version: v001 +target: + type: component + definition: + id: component_app_card + name: AppCard + props: + - name: app + type: GeneratedApp + required: true + description: Generated app to display + - name: showActions + type: boolean + required: false + default: true + description: Show delete button + events: + - name: onClick + payload: string + description: Fired when card clicked, payload is app ID + - name: onDelete + payload: string + description: Fired when delete confirmed, payload is app ID + uses_apis: [] + uses_components: [] + internal_state: + - isDeleting + variants: + - default + - compact +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/AppCard.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_app_gallery.yml b/.workflow/versions/v001/contexts/component_app_gallery.yml new file mode 100644 index 0000000..2815918 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_app_gallery.yml @@ -0,0 +1,82 @@ +task_id: task_create_component_app_gallery +entity_id: component_app_gallery +generated_at: '2025-12-19T06:08:12.198727' +workflow_version: v001 +target: + type: component + definition: + id: component_app_gallery + name: AppGallery + props: + - name: apps + type: GeneratedApp[] + required: true + description: Array of generated apps to display + - name: isLoading + type: boolean + required: false + default: false + description: Whether apps are being loaded + events: + - name: onSelectApp + payload: string + description: Fired when app is clicked, payload is app ID + uses_apis: [] + uses_components: + - component_app_card + internal_state: [] + variants: + - grid + - list +related: + models: [] + apis: [] + components: + - id: component_app_card + definition: &id001 + id: component_app_card + name: AppCard + props: + - name: app + type: GeneratedApp + required: true + description: Generated app to display + - name: showActions + type: boolean + required: false + default: true + description: Show delete button + events: + - name: onClick + payload: string + description: Fired when card clicked, payload is app ID + - name: onDelete + payload: string + description: Fired when delete confirmed, payload is app ID + uses_apis: [] + uses_components: [] + internal_state: + - isDeleting + variants: + - default + - compact + external: false +dependencies: + entity_ids: + - component_app_card + definitions: + - id: component_app_card + type: component + definition: *id001 + external: false +files: + to_create: + - app/components/AppGallery.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_app_iframe_viewer.yml b/.workflow/versions/v001/contexts/component_app_iframe_viewer.yml new file mode 100644 index 0000000..9b439a7 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_app_iframe_viewer.yml @@ -0,0 +1,48 @@ +task_id: task_create_component_app_iframe_viewer +entity_id: component_app_iframe_viewer +generated_at: '2025-12-19T06:08:12.200530' +workflow_version: v001 +target: + type: component + definition: + id: component_app_iframe_viewer + name: AppIframeViewer + props: + - name: htmlContent + type: string + required: true + description: HTML content to render in iframe + - name: title + type: string + required: true + description: App title for accessibility + events: + - name: onLoadComplete + payload: void + description: Fired when iframe finishes loading + uses_apis: [] + uses_components: [] + internal_state: + - isLoading + - iframeRef + variants: + - default + - fullscreen +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/AppIframeViewer.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_audio_player.yml b/.workflow/versions/v001/contexts/component_audio_player.yml new file mode 100644 index 0000000..99bac2d --- /dev/null +++ b/.workflow/versions/v001/contexts/component_audio_player.yml @@ -0,0 +1,51 @@ +task_id: task_create_component_audio_player +entity_id: component_audio_player +generated_at: '2025-12-19T06:08:12.196639' +workflow_version: v001 +target: + type: component + definition: + id: component_audio_player + name: AudioPlayer + props: + - name: audioUrl + type: string + required: true + description: URL to audio file + - name: duration + type: integer + required: true + description: Duration in seconds + events: + - name: onPlayPause + payload: boolean + description: Fired when play/pause toggled, payload is isPlaying + - name: onSeek + payload: number + description: Fired when user seeks, payload is new time in seconds + uses_apis: [] + uses_components: [] + internal_state: + - isPlaying + - currentTime + - audioElement + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/AudioPlayer.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_features.yml b/.workflow/versions/v001/contexts/component_features.yml new file mode 100644 index 0000000..6728524 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_features.yml @@ -0,0 +1,34 @@ +task_id: task_create_component_features +entity_id: component_features +generated_at: '2025-12-19T06:08:12.189675' +workflow_version: v001 +target: + type: component + definition: + id: component_features + name: Features + props: [] + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/Features.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_header.yml b/.workflow/versions/v001/contexts/component_header.yml new file mode 100644 index 0000000..fcecbd5 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_header.yml @@ -0,0 +1,39 @@ +task_id: task_create_component_header +entity_id: component_header +generated_at: '2025-12-19T06:08:12.188015' +workflow_version: v001 +target: + type: component + definition: + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/Header.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_hero.yml b/.workflow/versions/v001/contexts/component_hero.yml new file mode 100644 index 0000000..9724819 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_hero.yml @@ -0,0 +1,37 @@ +task_id: task_create_component_hero +entity_id: component_hero +generated_at: '2025-12-19T06:08:12.189153' +workflow_version: v001 +target: + type: component + definition: + id: component_hero + name: Hero + props: [] + events: + - name: onGetStarted + payload: void + description: Fired when CTA button clicked + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/Hero.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_login_form.yml b/.workflow/versions/v001/contexts/component_login_form.yml new file mode 100644 index 0000000..8f53a56 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_login_form.yml @@ -0,0 +1,114 @@ +task_id: task_create_component_login_form +entity_id: component_login_form +generated_at: '2025-12-19T06:08:12.190206' +workflow_version: v001 +target: + type: component + definition: + id: component_login_form + name: LoginForm + props: [] + events: + - name: onSuccess + payload: User + description: Fired when login successful + - name: onError + payload: string + description: Fired when login fails + uses_apis: + - api_login_user + uses_components: [] + internal_state: + - email + - password + - isLoading + - error + variants: + - default +related: + models: [] + apis: + - id: api_login_user + definition: &id001 + id: api_login_user + method: POST + path: /api/auth/login + summary: Login user + description: Authenticate user with email and password, return session token + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: email + type: string + required: true + validations: + - email + description: User email + - name: password + type: string + required: true + description: User password + example: + email: user@example.com + password: securepass123 + responses: + - status: 200 + description: Login successful + schema: + type: object + properties: + - name: user + type: object + - name: token + type: string + example: + user: + id: 550e8400-e29b-41d4-a716-446655440000 + email: user@example.com + name: John Doe + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + - status: 401 + description: Invalid credentials + schema: + type: object + properties: + - name: error + type: string + example: + error: Invalid email or password + depends_on_models: + - model_user + depends_on_apis: [] + auth: + required: false + roles: [] + rate_limit: + requests: 20 + window: 1h + external: false + components: [] +dependencies: + entity_ids: + - api_login_user + definitions: + - id: api_login_user + type: api + definition: *id001 + external: false +files: + to_create: + - app/components/LoginForm.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_record_button.yml b/.workflow/versions/v001/contexts/component_record_button.yml new file mode 100644 index 0000000..94300a4 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_record_button.yml @@ -0,0 +1,52 @@ +task_id: task_create_component_record_button +entity_id: component_record_button +generated_at: '2025-12-19T06:08:12.193251' +workflow_version: v001 +target: + type: component + definition: + id: component_record_button + name: RecordButton + props: + - name: isRecording + type: boolean + required: true + description: Whether currently recording + - name: isTranscribing + type: boolean + required: false + default: false + description: Whether transcribing in real-time + events: + - name: onStartRecording + payload: void + description: Fired when recording starts + - name: onStopRecording + payload: Blob + description: Fired when recording stops, includes audio blob + uses_apis: [] + uses_components: [] + internal_state: + - mediaRecorder + - audioChunks + variants: + - default + - large +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/RecordButton.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_recording_card.yml b/.workflow/versions/v001/contexts/component_recording_card.yml new file mode 100644 index 0000000..66916cf --- /dev/null +++ b/.workflow/versions/v001/contexts/component_recording_card.yml @@ -0,0 +1,51 @@ +task_id: task_create_component_recording_card +entity_id: component_recording_card +generated_at: '2025-12-19T06:08:12.195845' +workflow_version: v001 +target: + type: component + definition: + id: component_recording_card + name: RecordingCard + props: + - name: recording + type: Recording + required: true + description: Recording to display + - name: showActions + type: boolean + required: false + default: true + description: Show edit/delete buttons + events: + - name: onClick + payload: string + description: Fired when card clicked, payload is recording ID + - name: onDelete + payload: string + description: Fired when delete confirmed, payload is recording ID + uses_apis: [] + uses_components: [] + internal_state: + - isDeleting + variants: + - default + - compact +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/RecordingCard.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_recording_list.yml b/.workflow/versions/v001/contexts/component_recording_list.yml new file mode 100644 index 0000000..9025e0a --- /dev/null +++ b/.workflow/versions/v001/contexts/component_recording_list.yml @@ -0,0 +1,85 @@ +task_id: task_create_component_recording_list +entity_id: component_recording_list +generated_at: '2025-12-19T06:08:12.194659' +workflow_version: v001 +target: + type: component + definition: + id: component_recording_list + name: RecordingList + props: + - name: recordings + type: Recording[] + required: true + description: Array of recordings to display + - name: isLoading + type: boolean + required: false + default: false + description: Whether recordings are being loaded + events: + - name: onSelectRecording + payload: string + description: Fired when recording is clicked, payload is recording ID + - name: onDeleteRecording + payload: string + description: Fired when delete confirmed, payload is recording ID + uses_apis: [] + uses_components: + - component_recording_card + internal_state: [] + variants: + - default + - compact +related: + models: [] + apis: [] + components: + - id: component_recording_card + definition: &id001 + id: component_recording_card + name: RecordingCard + props: + - name: recording + type: Recording + required: true + description: Recording to display + - name: showActions + type: boolean + required: false + default: true + description: Show edit/delete buttons + events: + - name: onClick + payload: string + description: Fired when card clicked, payload is recording ID + - name: onDelete + payload: string + description: Fired when delete confirmed, payload is recording ID + uses_apis: [] + uses_components: [] + internal_state: + - isDeleting + variants: + - default + - compact + external: false +dependencies: + entity_ids: + - component_recording_card + definitions: + - id: component_recording_card + type: component + definition: *id001 + external: false +files: + to_create: + - app/components/RecordingList.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_register_form.yml b/.workflow/versions/v001/contexts/component_register_form.yml new file mode 100644 index 0000000..491f0dc --- /dev/null +++ b/.workflow/versions/v001/contexts/component_register_form.yml @@ -0,0 +1,138 @@ +task_id: task_create_component_register_form +entity_id: component_register_form +generated_at: '2025-12-19T06:08:12.191625' +workflow_version: v001 +target: + type: component + definition: + id: component_register_form + name: RegisterForm + props: [] + events: + - name: onSuccess + payload: User + description: Fired when registration successful + - name: onError + payload: string + description: Fired when registration fails + uses_apis: + - api_register_user + uses_components: [] + internal_state: + - email + - name + - password + - confirmPassword + - isLoading + - error + variants: + - default +related: + models: [] + apis: + - id: api_register_user + definition: &id001 + id: api_register_user + method: POST + path: /api/auth/register + summary: Register a new user + description: Create a new user account with email and password + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: email + type: string + required: true + validations: + - email + description: User email address + - name: name + type: string + required: true + validations: + - min:1 + - max:100 + description: User display name + - name: password + type: string + required: true + validations: + - min:8 + description: Password (min 8 characters) + example: + email: user@example.com + name: John Doe + password: securepass123 + responses: + - status: 201 + description: User created successfully + schema: + type: object + properties: + - name: id + type: uuid + - name: email + type: string + - name: name + type: string + - name: created_at + type: datetime + example: + id: 550e8400-e29b-41d4-a716-446655440000 + email: user@example.com + name: John Doe + created_at: '2024-12-19T06:00:00Z' + - status: 400 + description: Validation error + schema: + type: object + properties: + - name: error + type: string + example: + error: Invalid email or password too short + - status: 409 + description: Email already exists + schema: + type: object + properties: + - name: error + type: string + example: + error: Email already registered + depends_on_models: + - model_user + depends_on_apis: [] + auth: + required: false + roles: [] + rate_limit: + requests: 10 + window: 1h + external: false + components: [] +dependencies: + entity_ids: + - api_register_user + definitions: + - id: api_register_user + type: api + definition: *id001 + external: false +files: + to_create: + - app/components/RegisterForm.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_sidebar.yml b/.workflow/versions/v001/contexts/component_sidebar.yml new file mode 100644 index 0000000..ff31f30 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_sidebar.yml @@ -0,0 +1,42 @@ +task_id: task_create_component_sidebar +entity_id: component_sidebar +generated_at: '2025-12-19T06:08:12.188568' +workflow_version: v001 +target: + type: component + definition: + id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: Current route path for highlighting + events: + - name: onNavigate + payload: string + description: Fired when navigation item clicked + uses_apis: [] + uses_components: [] + internal_state: + - isCollapsed + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/Sidebar.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_summary_display.yml b/.workflow/versions/v001/contexts/component_summary_display.yml new file mode 100644 index 0000000..e8c728d --- /dev/null +++ b/.workflow/versions/v001/contexts/component_summary_display.yml @@ -0,0 +1,43 @@ +task_id: task_create_component_summary_display +entity_id: component_summary_display +generated_at: '2025-12-19T06:08:12.198060' +workflow_version: v001 +target: + type: component + definition: + id: component_summary_display + name: SummaryDisplay + props: + - name: summary + type: string + required: true + description: AI-generated summary text + - name: isLoading + type: boolean + required: false + default: false + description: Whether summary is being generated + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/SummaryDisplay.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_transcript_viewer.yml b/.workflow/versions/v001/contexts/component_transcript_viewer.yml new file mode 100644 index 0000000..45af1e5 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_transcript_viewer.yml @@ -0,0 +1,44 @@ +task_id: task_create_component_transcript_viewer +entity_id: component_transcript_viewer +generated_at: '2025-12-19T06:08:12.197337' +workflow_version: v001 +target: + type: component + definition: + id: component_transcript_viewer + name: TranscriptViewer + props: + - name: transcript + type: string + required: true + description: Transcript text to display + - name: isLive + type: boolean + required: false + default: false + description: Whether showing live transcription + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + - live +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/TranscriptViewer.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/component_wake_word_indicator.yml b/.workflow/versions/v001/contexts/component_wake_word_indicator.yml new file mode 100644 index 0000000..a005d01 --- /dev/null +++ b/.workflow/versions/v001/contexts/component_wake_word_indicator.yml @@ -0,0 +1,46 @@ +task_id: task_create_component_wake_word_indicator +entity_id: component_wake_word_indicator +generated_at: '2025-12-19T06:08:12.193974' +workflow_version: v001 +target: + type: component + definition: + id: component_wake_word_indicator + name: WakeWordIndicator + props: + - name: isListening + type: boolean + required: true + description: Whether wake word detection is active + - name: wakeWord + type: string + required: true + description: The wake word phrase + events: + - name: onWakeWordDetected + payload: void + description: Fired when wake word is detected + uses_apis: [] + uses_components: [] + internal_state: + - lastDetectionTime + variants: + - default +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - app/components/WakeWordIndicator.tsx + reference: [] +acceptance: +- criterion: Component renders without errors + verification: Import and render in test +- criterion: Props are typed correctly + verification: TypeScript compilation +- criterion: Events fire correctly + verification: Test event handlers diff --git a/.workflow/versions/v001/contexts/model_generated_app.yml b/.workflow/versions/v001/contexts/model_generated_app.yml new file mode 100644 index 0000000..091c81c --- /dev/null +++ b/.workflow/versions/v001/contexts/model_generated_app.yml @@ -0,0 +1,234 @@ +task_id: task_create_model_generated_app +entity_id: model_generated_app +generated_at: '2025-12-19T06:08:12.140756' +workflow_version: v001 +target: + type: model + definition: + id: model_generated_app + name: GeneratedApp + description: AI-generated application from recording content + table_name: generated_apps + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique app identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this app + - name: recording_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Source recording that triggered generation + - name: title + type: string + constraints: + - not_null + description: App title from AI analysis + - name: description + type: text + constraints: [] + description: App description + - name: html_content + type: text + constraints: + - not_null + description: Complete HTML/CSS/JS for iframe rendering + - name: prd_content + type: text + constraints: [] + description: Product Requirements Document (PRD) generated by AI + - name: ui_ux_design + type: text + constraints: [] + description: UI/UX design notes from AI + - name: app_type + type: string + constraints: [] + description: Type of app determined by AI (e.g., todo, calculator, form) + - name: status + type: enum + enum_values: + - generating + - completed + - failed + constraints: + - not_null + - default + default: generating + description: Generation status + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + - type: belongs_to + target: model_recording + foreign_key: recording_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_apps_user_created + - fields: + - recording_id + unique: false + name: idx_apps_recording + timestamps: true + soft_delete: false + validations: [] +related: + models: + - id: model_user + definition: &id001 + id: model_user + name: User + description: Application user account with email/password authentication + table_name: users + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique user identifier + - name: email + type: string + constraints: + - unique + - not_null + - indexed + description: User email address for login + - name: name + type: string + constraints: + - not_null + description: User's display name + - name: password_hash + type: string + constraints: + - not_null + description: Bcrypt hashed password + relations: [] + indexes: + - fields: + - email + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: Invalid email format + - field: name + rule: min:1 + message: Name is required + - field: password_hash + rule: min:60 + message: Invalid password hash + external: false + - id: model_recording + definition: &id002 + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_user + - model_recording + definitions: + - id: model_user + type: model + definition: *id001 + external: false + - id: model_recording + type: model + definition: *id002 + external: false +files: + to_create: + - prisma/schema.prisma + - app/models/generatedapp.ts + reference: [] +acceptance: +- criterion: Model defined in Prisma schema + verification: Check prisma/schema.prisma +- criterion: TypeScript types exported + verification: Import type in test file +- criterion: Relations properly configured + verification: Check Prisma relations diff --git a/.workflow/versions/v001/contexts/model_recording.yml b/.workflow/versions/v001/contexts/model_recording.yml new file mode 100644 index 0000000..8058c09 --- /dev/null +++ b/.workflow/versions/v001/contexts/model_recording.yml @@ -0,0 +1,143 @@ +task_id: task_create_model_recording +entity_id: model_recording +generated_at: '2025-12-19T06:08:12.139030' +workflow_version: v001 +target: + type: model + definition: + id: model_recording + name: Recording + description: Voice recording with transcript and summary + table_name: recordings + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique recording identifier + - name: user_id + type: uuid + constraints: + - foreign_key + - not_null + - indexed + description: Owner of this recording + - name: title + type: string + constraints: + - not_null + description: Recording title (auto-generated or user-edited) + - name: audio_file_path + type: string + constraints: + - not_null + description: Path to audio file in MinIO/S3 + - name: duration + type: integer + constraints: + - not_null + description: Recording duration in seconds + - name: transcript + type: text + constraints: [] + description: Full transcript from Whisper STT + - name: summary + type: text + constraints: [] + description: AI-generated summary from Gemini + - name: is_transcribing + type: boolean + constraints: + - not_null + - default + default: false + description: Whether currently transcribing + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: + - user_id + - created_at + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: min:1 + message: Duration must be at least 1 second +related: + models: + - id: model_user + definition: &id001 + id: model_user + name: User + description: Application user account with email/password authentication + table_name: users + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique user identifier + - name: email + type: string + constraints: + - unique + - not_null + - indexed + description: User email address for login + - name: name + type: string + constraints: + - not_null + description: User's display name + - name: password_hash + type: string + constraints: + - not_null + description: Bcrypt hashed password + relations: [] + indexes: + - fields: + - email + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: Invalid email format + - field: name + rule: min:1 + message: Name is required + - field: password_hash + rule: min:60 + message: Invalid password hash + external: false + apis: [] + components: [] +dependencies: + entity_ids: + - model_user + definitions: + - id: model_user + type: model + definition: *id001 + external: false +files: + to_create: + - prisma/schema.prisma + - app/models/recording.ts + reference: [] +acceptance: +- criterion: Model defined in Prisma schema + verification: Check prisma/schema.prisma +- criterion: TypeScript types exported + verification: Import type in test file +- criterion: Relations properly configured + verification: Check Prisma relations diff --git a/.workflow/versions/v001/contexts/model_user.yml b/.workflow/versions/v001/contexts/model_user.yml new file mode 100644 index 0000000..9d0f058 --- /dev/null +++ b/.workflow/versions/v001/contexts/model_user.yml @@ -0,0 +1,71 @@ +task_id: task_create_model_user +entity_id: model_user +generated_at: '2025-12-19T06:08:12.138093' +workflow_version: v001 +target: + type: model + definition: + id: model_user + name: User + description: Application user account with email/password authentication + table_name: users + fields: + - name: id + type: uuid + constraints: + - primary_key + description: Unique user identifier + - name: email + type: string + constraints: + - unique + - not_null + - indexed + description: User email address for login + - name: name + type: string + constraints: + - not_null + description: User's display name + - name: password_hash + type: string + constraints: + - not_null + description: Bcrypt hashed password + relations: [] + indexes: + - fields: + - email + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: Invalid email format + - field: name + rule: min:1 + message: Name is required + - field: password_hash + rule: min:60 + message: Invalid password hash +related: + models: [] + apis: [] + components: [] +dependencies: + entity_ids: [] + definitions: [] +files: + to_create: + - prisma/schema.prisma + - app/models/user.ts + reference: [] +acceptance: +- criterion: Model defined in Prisma schema + verification: Check prisma/schema.prisma +- criterion: TypeScript types exported + verification: Import type in test file +- criterion: Relations properly configured + verification: Check Prisma relations diff --git a/.workflow/versions/v001/contexts/page_app_detail.yml b/.workflow/versions/v001/contexts/page_app_detail.yml new file mode 100644 index 0000000..bc3bc7e --- /dev/null +++ b/.workflow/versions/v001/contexts/page_app_detail.yml @@ -0,0 +1,189 @@ +task_id: task_create_page_app_detail +entity_id: page_app_detail +generated_at: '2025-12-19T06:08:12.185761' +workflow_version: v001 +target: + type: page + definition: + id: page_app_detail + name: App Preview + path: /apps/[id] + layout: layout_app + data_needs: + - api_id: api_get_app + purpose: Display app in iframe + on_load: true + components: + - component_header + - component_sidebar + - component_app_iframe_viewer + seo: + title: App Preview - NoteToApp + description: View generated application + auth: + required: true + roles: [] + redirect: /login + state: + local: + - isLoading + global: + - user +related: + models: [] + apis: + - id: api_get_app + definition: &id003 + id: api_get_app + method: GET + path: /api/apps/[id] + summary: Get single generated app + description: Get app details including HTML content for iframe rendering + tags: + - apps + path_params: + - name: id + type: uuid + description: App ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: App details + schema: + type: object + properties: + - name: id + type: uuid + - name: recording_id + type: uuid + - name: title + type: string + - name: description + type: string + - name: html_content + type: string + - name: app_type + type: string + - name: status + type: string + - name: created_at + type: datetime + - status: 404 + description: App not found + schema: + type: object + properties: + - name: error + type: string + example: + error: App not found + depends_on_models: + - model_generated_app + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + components: + - id: component_header + definition: &id001 + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_app_iframe_viewer + definition: &id002 + id: component_app_iframe_viewer + name: AppIframeViewer + props: + - name: htmlContent + type: string + required: true + description: HTML content to render in iframe + - name: title + type: string + required: true + description: App title for accessibility + events: + - name: onLoadComplete + payload: void + description: Fired when iframe finishes loading + uses_apis: [] + uses_components: [] + internal_state: + - isLoading + - iframeRef + variants: + - default + - fullscreen + external: false + - id: component_sidebar + definition: &id004 + id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: Current route path for highlighting + events: + - name: onNavigate + payload: string + description: Fired when navigation item clicked + uses_apis: [] + uses_components: [] + internal_state: + - isCollapsed + variants: + - default + external: false +dependencies: + entity_ids: + - component_header + - component_app_iframe_viewer + - api_get_app + - component_sidebar + definitions: + - id: component_header + type: component + definition: *id001 + external: false + - id: component_app_iframe_viewer + type: component + definition: *id002 + external: false + - id: api_get_app + type: api + definition: *id003 + external: false + - id: component_sidebar + type: component + definition: *id004 + external: false +files: + to_create: + - app/apps/[id]/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /apps/[id] + verification: Navigate to /apps/[id] +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_apps.yml b/.workflow/versions/v001/contexts/page_apps.yml new file mode 100644 index 0000000..906da92 --- /dev/null +++ b/.workflow/versions/v001/contexts/page_apps.yml @@ -0,0 +1,218 @@ +task_id: task_create_page_apps +entity_id: page_apps +generated_at: '2025-12-19T06:08:12.183205' +workflow_version: v001 +target: + type: page + definition: + id: page_apps + name: Generated Apps + path: /apps + layout: layout_app + data_needs: + - api_id: api_list_apps + purpose: Display generated apps gallery + on_load: true + components: + - component_header + - component_sidebar + - component_app_gallery + - component_app_card + seo: + title: Generated Apps - NoteToApp + description: Your AI-generated applications + auth: + required: true + roles: [] + redirect: /login + state: + local: + - filterType + global: + - user +related: + models: [] + apis: + - id: api_list_apps + definition: &id001 + id: api_list_apps + method: GET + path: /api/apps + summary: List generated apps + description: Get all apps generated for authenticated user + tags: + - apps + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: Maximum number of apps to return + - name: offset + type: integer + required: false + default: 0 + description: Number of apps to skip + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Apps list + schema: + type: object + properties: + - name: apps + type: array + - name: total + type: integer + example: + apps: + - id: app-123 + title: Todo App + app_type: todo + status: completed + created_at: '2024-12-19T06:00:00Z' + total: 12 + depends_on_models: + - model_generated_app + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + components: + - id: component_app_card + definition: &id002 + id: component_app_card + name: AppCard + props: + - name: app + type: GeneratedApp + required: true + description: Generated app to display + - name: showActions + type: boolean + required: false + default: true + description: Show delete button + events: + - name: onClick + payload: string + description: Fired when card clicked, payload is app ID + - name: onDelete + payload: string + description: Fired when delete confirmed, payload is app ID + uses_apis: [] + uses_components: [] + internal_state: + - isDeleting + variants: + - default + - compact + external: false + - id: component_app_gallery + definition: &id003 + id: component_app_gallery + name: AppGallery + props: + - name: apps + type: GeneratedApp[] + required: true + description: Array of generated apps to display + - name: isLoading + type: boolean + required: false + default: false + description: Whether apps are being loaded + events: + - name: onSelectApp + payload: string + description: Fired when app is clicked, payload is app ID + uses_apis: [] + uses_components: + - component_app_card + internal_state: [] + variants: + - grid + - list + external: false + - id: component_header + definition: &id004 + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_sidebar + definition: &id005 + id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: Current route path for highlighting + events: + - name: onNavigate + payload: string + description: Fired when navigation item clicked + uses_apis: [] + uses_components: [] + internal_state: + - isCollapsed + variants: + - default + external: false +dependencies: + entity_ids: + - api_list_apps + - component_app_card + - component_app_gallery + - component_header + - component_sidebar + definitions: + - id: api_list_apps + type: api + definition: *id001 + external: false + - id: component_app_card + type: component + definition: *id002 + external: false + - id: component_app_gallery + type: component + definition: *id003 + external: false + - id: component_header + type: component + definition: *id004 + external: false + - id: component_sidebar + type: component + definition: *id005 + external: false +files: + to_create: + - app/apps/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /apps + verification: Navigate to /apps +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_dashboard.yml b/.workflow/versions/v001/contexts/page_dashboard.yml new file mode 100644 index 0000000..7a25e3c --- /dev/null +++ b/.workflow/versions/v001/contexts/page_dashboard.yml @@ -0,0 +1,312 @@ +task_id: task_create_page_dashboard +entity_id: page_dashboard +generated_at: '2025-12-19T06:08:12.173922' +workflow_version: v001 +target: + type: page + definition: + id: page_dashboard + name: Dashboard + path: /dashboard + layout: layout_app + data_needs: + - api_id: api_get_current_user + purpose: Display user info + on_load: true + - api_id: api_list_recordings + purpose: Show recent recordings + on_load: true + components: + - component_header + - component_sidebar + - component_record_button + - component_wake_word_indicator + - component_recording_list + seo: + title: Dashboard - NoteToApp + description: Your voice recordings and generated apps + auth: + required: true + roles: [] + redirect: /login + state: + local: + - isRecording + - isListening + - currentRecording + global: + - user +related: + models: [] + apis: + - id: api_list_recordings + definition: &id002 + id: api_list_recordings + method: GET + path: /api/recordings + summary: List user recordings + description: Get all recordings for authenticated user, sorted by creation date + tags: + - recordings + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: Maximum number of recordings to return + - name: offset + type: integer + required: false + default: 0 + description: Number of recordings to skip + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Recordings list + schema: + type: object + properties: + - name: recordings + type: array + - name: total + type: integer + example: + recordings: + - id: rec-123 + title: Meeting Notes + duration: 180 + created_at: '2024-12-19T06:00:00Z' + total: 42 + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + - id: api_get_current_user + definition: &id004 + id: api_get_current_user + method: GET + path: /api/auth/me + summary: Get current user + description: Get currently authenticated user information + tags: + - auth + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: User data + schema: + type: object + properties: + - name: id + type: uuid + - name: email + type: string + - name: name + type: string + - name: created_at + type: datetime + example: + id: 550e8400-e29b-41d4-a716-446655440000 + email: user@example.com + name: John Doe + created_at: '2024-12-19T06:00:00Z' + - status: 401 + description: Not authenticated + schema: + type: object + properties: + - name: error + type: string + example: + error: Not authenticated + depends_on_models: + - model_user + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + components: + - id: component_recording_list + definition: &id001 + id: component_recording_list + name: RecordingList + props: + - name: recordings + type: Recording[] + required: true + description: Array of recordings to display + - name: isLoading + type: boolean + required: false + default: false + description: Whether recordings are being loaded + events: + - name: onSelectRecording + payload: string + description: Fired when recording is clicked, payload is recording ID + - name: onDeleteRecording + payload: string + description: Fired when delete confirmed, payload is recording ID + uses_apis: [] + uses_components: + - component_recording_card + internal_state: [] + variants: + - default + - compact + external: false + - id: component_record_button + definition: &id003 + id: component_record_button + name: RecordButton + props: + - name: isRecording + type: boolean + required: true + description: Whether currently recording + - name: isTranscribing + type: boolean + required: false + default: false + description: Whether transcribing in real-time + events: + - name: onStartRecording + payload: void + description: Fired when recording starts + - name: onStopRecording + payload: Blob + description: Fired when recording stops, includes audio blob + uses_apis: [] + uses_components: [] + internal_state: + - mediaRecorder + - audioChunks + variants: + - default + - large + external: false + - id: component_wake_word_indicator + definition: &id005 + id: component_wake_word_indicator + name: WakeWordIndicator + props: + - name: isListening + type: boolean + required: true + description: Whether wake word detection is active + - name: wakeWord + type: string + required: true + description: The wake word phrase + events: + - name: onWakeWordDetected + payload: void + description: Fired when wake word is detected + uses_apis: [] + uses_components: [] + internal_state: + - lastDetectionTime + variants: + - default + external: false + - id: component_header + definition: &id006 + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_sidebar + definition: &id007 + id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: Current route path for highlighting + events: + - name: onNavigate + payload: string + description: Fired when navigation item clicked + uses_apis: [] + uses_components: [] + internal_state: + - isCollapsed + variants: + - default + external: false +dependencies: + entity_ids: + - component_recording_list + - api_list_recordings + - component_record_button + - api_get_current_user + - component_wake_word_indicator + - component_header + - component_sidebar + definitions: + - id: component_recording_list + type: component + definition: *id001 + external: false + - id: api_list_recordings + type: api + definition: *id002 + external: false + - id: component_record_button + type: component + definition: *id003 + external: false + - id: api_get_current_user + type: api + definition: *id004 + external: false + - id: component_wake_word_indicator + type: component + definition: *id005 + external: false + - id: component_header + type: component + definition: *id006 + external: false + - id: component_sidebar + type: component + definition: *id007 + external: false +files: + to_create: + - app/dashboard/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /dashboard + verification: Navigate to /dashboard +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_home.yml b/.workflow/versions/v001/contexts/page_home.yml new file mode 100644 index 0000000..f34d1db --- /dev/null +++ b/.workflow/versions/v001/contexts/page_home.yml @@ -0,0 +1,103 @@ +task_id: task_create_page_home +entity_id: page_home +generated_at: '2025-12-19T06:08:12.170818' +workflow_version: v001 +target: + type: page + definition: + id: page_home + name: Home + path: / + layout: layout_public + data_needs: [] + components: + - component_header + - component_hero + - component_features + seo: + title: NoteToApp - Voice Recording with AI Summarization + description: Record voice notes, get AI summaries, and generate apps automatically + auth: + required: false + roles: [] + redirect: '' + state: + local: [] + global: [] +related: + models: [] + apis: [] + components: + - id: component_features + definition: &id001 + id: component_features + name: Features + props: [] + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_header + definition: &id002 + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_hero + definition: &id003 + id: component_hero + name: Hero + props: [] + events: + - name: onGetStarted + payload: void + description: Fired when CTA button clicked + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false +dependencies: + entity_ids: + - component_features + - component_header + - component_hero + definitions: + - id: component_features + type: component + definition: *id001 + external: false + - id: component_header + type: component + definition: *id002 + external: false + - id: component_hero + type: component + definition: *id003 + external: false +files: + to_create: + - app//page.tsx + reference: [] +acceptance: +- criterion: Page renders at / + verification: Navigate to / +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_login.yml b/.workflow/versions/v001/contexts/page_login.yml new file mode 100644 index 0000000..c4e9f86 --- /dev/null +++ b/.workflow/versions/v001/contexts/page_login.yml @@ -0,0 +1,72 @@ +task_id: task_create_page_login +entity_id: page_login +generated_at: '2025-12-19T06:08:12.172043' +workflow_version: v001 +target: + type: page + definition: + id: page_login + name: Login + path: /login + layout: layout_auth + data_needs: [] + components: + - component_login_form + seo: + title: Login - NoteToApp + description: Login to your account + auth: + required: false + roles: [] + redirect: '' + state: + local: + - error + - isLoading + global: [] +related: + models: [] + apis: [] + components: + - id: component_login_form + definition: &id001 + id: component_login_form + name: LoginForm + props: [] + events: + - name: onSuccess + payload: User + description: Fired when login successful + - name: onError + payload: string + description: Fired when login fails + uses_apis: + - api_login_user + uses_components: [] + internal_state: + - email + - password + - isLoading + - error + variants: + - default + external: false +dependencies: + entity_ids: + - component_login_form + definitions: + - id: component_login_form + type: component + definition: *id001 + external: false +files: + to_create: + - app/login/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /login + verification: Navigate to /login +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_recording_detail.yml b/.workflow/versions/v001/contexts/page_recording_detail.yml new file mode 100644 index 0000000..2239a01 --- /dev/null +++ b/.workflow/versions/v001/contexts/page_recording_detail.yml @@ -0,0 +1,249 @@ +task_id: task_create_page_recording_detail +entity_id: page_recording_detail +generated_at: '2025-12-19T06:08:12.180266' +workflow_version: v001 +target: + type: page + definition: + id: page_recording_detail + name: Recording Detail + path: /recordings/[id] + layout: layout_app + data_needs: + - api_id: api_get_recording + purpose: Display recording details + on_load: true + components: + - component_header + - component_sidebar + - component_audio_player + - component_transcript_viewer + - component_summary_display + seo: + title: Recording Detail - NoteToApp + description: View recording transcript and summary + auth: + required: true + roles: [] + redirect: /login + state: + local: + - isPlaying + - currentTime + - isTranscribing + global: + - user +related: + models: [] + apis: + - id: api_get_recording + definition: &id003 + id: api_get_recording + method: GET + path: /api/recordings/[id] + summary: Get single recording + description: Get recording details including transcript and summary + tags: + - recordings + path_params: + - name: id + type: uuid + description: Recording ID + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Recording details + schema: + type: object + properties: + - name: id + type: uuid + - name: title + type: string + - name: audio_file_path + type: string + - name: duration + type: integer + - name: transcript + type: string + - name: summary + type: string + - name: is_transcribing + type: boolean + - name: created_at + type: datetime + - status: 404 + description: Recording not found + schema: + type: object + properties: + - name: error + type: string + example: + error: Recording not found + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + components: + - id: component_transcript_viewer + definition: &id001 + id: component_transcript_viewer + name: TranscriptViewer + props: + - name: transcript + type: string + required: true + description: Transcript text to display + - name: isLive + type: boolean + required: false + default: false + description: Whether showing live transcription + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + - live + external: false + - id: component_summary_display + definition: &id002 + id: component_summary_display + name: SummaryDisplay + props: + - name: summary + type: string + required: true + description: AI-generated summary text + - name: isLoading + type: boolean + required: false + default: false + description: Whether summary is being generated + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_audio_player + definition: &id004 + id: component_audio_player + name: AudioPlayer + props: + - name: audioUrl + type: string + required: true + description: URL to audio file + - name: duration + type: integer + required: true + description: Duration in seconds + events: + - name: onPlayPause + payload: boolean + description: Fired when play/pause toggled, payload is isPlaying + - name: onSeek + payload: number + description: Fired when user seeks, payload is new time in seconds + uses_apis: [] + uses_components: [] + internal_state: + - isPlaying + - currentTime + - audioElement + variants: + - default + external: false + - id: component_header + definition: &id005 + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_sidebar + definition: &id006 + id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: Current route path for highlighting + events: + - name: onNavigate + payload: string + description: Fired when navigation item clicked + uses_apis: [] + uses_components: [] + internal_state: + - isCollapsed + variants: + - default + external: false +dependencies: + entity_ids: + - component_transcript_viewer + - component_summary_display + - api_get_recording + - component_audio_player + - component_header + - component_sidebar + definitions: + - id: component_transcript_viewer + type: component + definition: *id001 + external: false + - id: component_summary_display + type: component + definition: *id002 + external: false + - id: api_get_recording + type: api + definition: *id003 + external: false + - id: component_audio_player + type: component + definition: *id004 + external: false + - id: component_header + type: component + definition: *id005 + external: false + - id: component_sidebar + type: component + definition: *id006 + external: false +files: + to_create: + - app/recordings/[id]/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /recordings/[id] + verification: Navigate to /recordings/[id] +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_recordings.yml b/.workflow/versions/v001/contexts/page_recordings.yml new file mode 100644 index 0000000..3bbb8e7 --- /dev/null +++ b/.workflow/versions/v001/contexts/page_recordings.yml @@ -0,0 +1,221 @@ +task_id: task_create_page_recordings +entity_id: page_recordings +generated_at: '2025-12-19T06:08:12.177618' +workflow_version: v001 +target: + type: page + definition: + id: page_recordings + name: Recordings + path: /recordings + layout: layout_app + data_needs: + - api_id: api_list_recordings + purpose: Display all recordings + on_load: true + components: + - component_header + - component_sidebar + - component_recording_list + - component_recording_card + seo: + title: Recordings - NoteToApp + description: All your voice recordings + auth: + required: true + roles: [] + redirect: /login + state: + local: + - searchQuery + - sortBy + global: + - user +related: + models: [] + apis: + - id: api_list_recordings + definition: &id001 + id: api_list_recordings + method: GET + path: /api/recordings + summary: List user recordings + description: Get all recordings for authenticated user, sorted by creation date + tags: + - recordings + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: Maximum number of recordings to return + - name: offset + type: integer + required: false + default: 0 + description: Number of recordings to skip + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: Recordings list + schema: + type: object + properties: + - name: recordings + type: array + - name: total + type: integer + example: + recordings: + - id: rec-123 + title: Meeting Notes + duration: 180 + created_at: '2024-12-19T06:00:00Z' + total: 42 + depends_on_models: + - model_recording + depends_on_apis: [] + auth: + required: true + roles: [] + external: false + components: + - id: component_header + definition: &id002 + id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: Current user for displaying name/avatar + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: + - default + external: false + - id: component_recording_list + definition: &id003 + id: component_recording_list + name: RecordingList + props: + - name: recordings + type: Recording[] + required: true + description: Array of recordings to display + - name: isLoading + type: boolean + required: false + default: false + description: Whether recordings are being loaded + events: + - name: onSelectRecording + payload: string + description: Fired when recording is clicked, payload is recording ID + - name: onDeleteRecording + payload: string + description: Fired when delete confirmed, payload is recording ID + uses_apis: [] + uses_components: + - component_recording_card + internal_state: [] + variants: + - default + - compact + external: false + - id: component_recording_card + definition: &id004 + id: component_recording_card + name: RecordingCard + props: + - name: recording + type: Recording + required: true + description: Recording to display + - name: showActions + type: boolean + required: false + default: true + description: Show edit/delete buttons + events: + - name: onClick + payload: string + description: Fired when card clicked, payload is recording ID + - name: onDelete + payload: string + description: Fired when delete confirmed, payload is recording ID + uses_apis: [] + uses_components: [] + internal_state: + - isDeleting + variants: + - default + - compact + external: false + - id: component_sidebar + definition: &id005 + id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: Current route path for highlighting + events: + - name: onNavigate + payload: string + description: Fired when navigation item clicked + uses_apis: [] + uses_components: [] + internal_state: + - isCollapsed + variants: + - default + external: false +dependencies: + entity_ids: + - api_list_recordings + - component_header + - component_recording_list + - component_recording_card + - component_sidebar + definitions: + - id: api_list_recordings + type: api + definition: *id001 + external: false + - id: component_header + type: component + definition: *id002 + external: false + - id: component_recording_list + type: component + definition: *id003 + external: false + - id: component_recording_card + type: component + definition: *id004 + external: false + - id: component_sidebar + type: component + definition: *id005 + external: false +files: + to_create: + - app/recordings/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /recordings + verification: Navigate to /recordings +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contexts/page_register.yml b/.workflow/versions/v001/contexts/page_register.yml new file mode 100644 index 0000000..15dfbaf --- /dev/null +++ b/.workflow/versions/v001/contexts/page_register.yml @@ -0,0 +1,74 @@ +task_id: task_create_page_register +entity_id: page_register +generated_at: '2025-12-19T06:08:12.172995' +workflow_version: v001 +target: + type: page + definition: + id: page_register + name: Register + path: /register + layout: layout_auth + data_needs: [] + components: + - component_register_form + seo: + title: Register - NoteToApp + description: Create a new account + auth: + required: false + roles: [] + redirect: '' + state: + local: + - error + - isLoading + global: [] +related: + models: [] + apis: [] + components: + - id: component_register_form + definition: &id001 + id: component_register_form + name: RegisterForm + props: [] + events: + - name: onSuccess + payload: User + description: Fired when registration successful + - name: onError + payload: string + description: Fired when registration fails + uses_apis: + - api_register_user + uses_components: [] + internal_state: + - email + - name + - password + - confirmPassword + - isLoading + - error + variants: + - default + external: false +dependencies: + entity_ids: + - component_register_form + definitions: + - id: component_register_form + type: component + definition: *id001 + external: false +files: + to_create: + - app/register/page.tsx + reference: [] +acceptance: +- criterion: Page renders at /register + verification: Navigate to /register +- criterion: Data fetching works + verification: Check network tab +- criterion: Components render correctly + verification: Visual inspection diff --git a/.workflow/versions/v001/contracts/api_contract.yml b/.workflow/versions/v001/contracts/api_contract.yml new file mode 100644 index 0000000..8f25857 --- /dev/null +++ b/.workflow/versions/v001/contracts/api_contract.yml @@ -0,0 +1,907 @@ +api_contract: + workflow_version: v001 + design_document_revision: 1 + generated_at: '2025-12-19T06:08:18.844037' + validated_at: null + status: draft +types: +- id: type_User + name: User + definition: + type: object + properties: + - name: id + type: string + required: true + description: Unique user identifier + - name: email + type: string + required: true + description: User email address for login + - name: name + type: string + required: true + description: User's display name + used_by: + models: + - model_user + responses: [] + requests: [] +- id: type_Recording + name: Recording + definition: + type: object + properties: + - name: id + type: string + required: true + description: Unique recording identifier + - name: userId + type: string + required: true + description: Owner of this recording + - name: title + type: string + required: true + description: Recording title (auto-generated or user-edited) + - name: audioFilePath + type: string + required: true + description: Path to audio file in MinIO/S3 + - name: duration + type: number + required: true + description: Recording duration in seconds + - name: transcript + type: string + required: false + description: Full transcript from Whisper STT + - name: summary + type: string + required: false + description: AI-generated summary from Gemini + - name: isTranscribing + type: boolean + required: true + description: Whether currently transcribing + used_by: + models: + - model_recording + responses: [] + requests: [] +- id: type_GeneratedApp + name: GeneratedApp + definition: + type: object + properties: + - name: id + type: string + required: true + description: Unique app identifier + - name: userId + type: string + required: true + description: Owner of this app + - name: recordingId + type: string + required: true + description: Source recording that triggered generation + - name: title + type: string + required: true + description: App title from AI analysis + - name: description + type: string + required: false + description: App description + - name: htmlContent + type: string + required: true + description: Complete HTML/CSS/JS for iframe rendering + - name: prdContent + type: string + required: false + description: Product Requirements Document (PRD) generated by AI + - name: uiUxDesign + type: string + required: false + description: UI/UX design notes from AI + - name: appType + type: string + required: false + description: Type of app determined by AI (e.g., todo, calculator, form) + - name: status + type: '''generating'' | ''completed'' | ''failed''' + required: true + description: Generation status + used_by: + models: + - model_generated_app + responses: [] + requests: [] +- id: type_RegisterUserRequest + name: RegisterUserRequest + definition: + type: object + properties: + - name: email + type: string + required: true + description: User email address + validation: email + - name: name + type: string + required: true + description: User display name + validation: min:1,max:100 + - name: password + type: string + required: true + description: Password (min 8 characters) + validation: min:8 + used_by: + models: [] + responses: [] + requests: + - api_register_user +- id: type_LoginUserRequest + name: LoginUserRequest + definition: + type: object + properties: + - name: email + type: string + required: true + description: User email + validation: email + - name: password + type: string + required: true + description: User password + validation: null + used_by: + models: [] + responses: [] + requests: + - api_login_user +- id: type_LogoutUserRequest + name: LogoutUserRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_logout_user +- id: type_LogoutUserResponse + name: LogoutUserResponse + definition: + type: object + properties: + - name: success + type: boolean + required: true + description: '' + used_by: + models: [] + responses: + - api_logout_user + requests: [] +- id: type_GetCurrentUserRequest + name: GetCurrentUserRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_get_current_user +- id: type_ListRecordingsRequest + name: ListRecordingsRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_list_recordings +- id: type_CreateRecordingRequest + name: CreateRecordingRequest + definition: + type: object + properties: + - name: audio + type: unknown + required: true + description: Audio file (webm, mp3, wav) + validation: null + - name: title + type: string + required: false + description: Recording title (auto-generated if not provided) + validation: null + - name: duration + type: number + required: true + description: Duration in seconds + validation: null + used_by: + models: [] + responses: [] + requests: + - api_create_recording +- id: type_GetRecordingRequest + name: GetRecordingRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_get_recording +- id: type_DeleteRecordingRequest + name: DeleteRecordingRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_delete_recording +- id: type_TranscribeRecordingRequest + name: TranscribeRecordingRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_transcribe_recording +- id: type_SummarizeRecordingRequest + name: SummarizeRecordingRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_summarize_recording +- id: type_ListAppsRequest + name: ListAppsRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_list_apps +- id: type_GenerateAppRequest + name: GenerateAppRequest + definition: + type: object + properties: + - name: recordingId + type: string + required: true + description: Source recording ID + validation: null + used_by: + models: [] + responses: [] + requests: + - api_generate_app +- id: type_GetAppRequest + name: GetAppRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_get_app +- id: type_DeleteAppRequest + name: DeleteAppRequest + definition: + type: object + properties: [] + used_by: + models: [] + responses: [] + requests: + - api_delete_app +endpoints: +- id: api_register_user + method: POST + path: /api/auth/register + path_params: [] + query_params: [] + request_body: + type_id: type_RegisterUserRequest + content_type: application/json + response: + success: + status: 201 + type_id: type_User + is_array: false + errors: + - status: 400 + type_id: type_ApiError + description: Validation error + - status: 409 + type_id: type_ApiError + description: Email already exists + auth: + required: false + roles: &id001 [] + version: 1.0.0 +- id: api_login_user + method: POST + path: /api/auth/login + path_params: [] + query_params: [] + request_body: + type_id: type_LoginUserRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_User + is_array: false + errors: + - status: 401 + type_id: type_ApiError + description: Invalid credentials + auth: + required: false + roles: &id002 [] + version: 1.0.0 +- id: api_logout_user + method: POST + path: /api/auth/logout + path_params: [] + query_params: [] + request_body: + type_id: type_LogoutUserRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_LogoutUserResponse + is_array: false + errors: [] + auth: + required: true + roles: &id003 [] + version: 1.0.0 +- id: api_get_current_user + method: GET + path: /api/auth/me + path_params: [] + query_params: [] + request_body: + type_id: type_GetCurrentUserRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_User + is_array: false + errors: + - status: 401 + type_id: type_ApiError + description: Not authenticated + auth: + required: true + roles: &id004 [] + version: 1.0.0 +- id: api_list_recordings + method: GET + path: /api/recordings + path_params: [] + query_params: + - name: limit + type: number + required: false + default: 50 + description: Maximum number of recordings to return + - name: offset + type: number + required: false + default: 0 + description: Number of recordings to skip + request_body: + type_id: type_ListRecordingsRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_Recording + is_array: false + errors: [] + auth: + required: true + roles: &id005 [] + version: 1.0.0 +- id: api_create_recording + method: POST + path: /api/recordings + path_params: [] + query_params: [] + request_body: + type_id: type_CreateRecordingRequest + content_type: application/json + response: + success: + status: 201 + type_id: type_Recording + is_array: false + errors: + - status: 400 + type_id: type_ApiError + description: Invalid audio file + auth: + required: true + roles: &id006 [] + version: 1.0.0 +- id: api_get_recording + method: GET + path: /api/recordings/[id] + path_params: + - name: id + type: string + description: Recording ID + query_params: [] + request_body: + type_id: type_GetRecordingRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_Recording + is_array: false + errors: + - status: 404 + type_id: type_ApiError + description: Recording not found + auth: + required: true + roles: &id007 [] + version: 1.0.0 +- id: api_delete_recording + method: DELETE + path: /api/recordings/[id] + path_params: + - name: id + type: string + description: Recording ID + query_params: [] + request_body: + type_id: type_DeleteRecordingRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_Recording + is_array: false + errors: + - status: 404 + type_id: type_ApiError + description: Recording not found + auth: + required: true + roles: &id008 [] + version: 1.0.0 +- id: api_transcribe_recording + method: POST + path: /api/recordings/[id]/transcribe + path_params: + - name: id + type: string + description: Recording ID + query_params: [] + request_body: + type_id: type_TranscribeRecordingRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_Recording + is_array: false + errors: + - status: 404 + type_id: type_ApiError + description: Recording not found + auth: + required: true + roles: &id009 [] + version: 1.0.0 +- id: api_summarize_recording + method: POST + path: /api/recordings/[id]/summarize + path_params: + - name: id + type: string + description: Recording ID + query_params: [] + request_body: + type_id: type_SummarizeRecordingRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_Recording + is_array: false + errors: + - status: 400 + type_id: type_ApiError + description: No transcript available + - status: 404 + type_id: type_ApiError + description: Recording not found + auth: + required: true + roles: &id010 [] + version: 1.0.0 +- id: api_list_apps + method: GET + path: /api/apps + path_params: [] + query_params: + - name: limit + type: number + required: false + default: 50 + description: Maximum number of apps to return + - name: offset + type: number + required: false + default: 0 + description: Number of apps to skip + request_body: + type_id: type_ListAppsRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_GeneratedApp + is_array: false + errors: [] + auth: + required: true + roles: &id011 [] + version: 1.0.0 +- id: api_generate_app + method: POST + path: /api/apps/generate + path_params: [] + query_params: [] + request_body: + type_id: type_GenerateAppRequest + content_type: application/json + response: + success: + status: 201 + type_id: type_GeneratedApp + is_array: false + errors: + - status: 400 + type_id: type_ApiError + description: Recording has no summary + - status: 404 + type_id: type_ApiError + description: Recording not found + auth: + required: true + roles: &id012 [] + version: 1.0.0 +- id: api_get_app + method: GET + path: /api/apps/[id] + path_params: + - name: id + type: string + description: App ID + query_params: [] + request_body: + type_id: type_GetAppRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_GeneratedApp + is_array: false + errors: + - status: 404 + type_id: type_ApiError + description: App not found + auth: + required: true + roles: &id013 [] + version: 1.0.0 +- id: api_delete_app + method: DELETE + path: /api/apps/[id] + path_params: + - name: id + type: string + description: App ID + query_params: [] + request_body: + type_id: type_DeleteAppRequest + content_type: application/json + response: + success: + status: 200 + type_id: type_GeneratedApp + is_array: false + errors: + - status: 404 + type_id: type_ApiError + description: App not found + auth: + required: true + roles: &id014 [] + version: 1.0.0 +frontend_calls: +- id: call_page_dashboard_api_get_current_user + source: + entity_id: page_dashboard + file_path: app/dashboard/page.tsx + endpoint_id: api_get_current_user + purpose: Display user info + trigger: onLoad + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Update state + error_action: Show error +- id: call_page_dashboard_api_list_recordings + source: + entity_id: page_dashboard + file_path: app/dashboard/page.tsx + endpoint_id: api_list_recordings + purpose: Show recent recordings + trigger: onLoad + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Update state + error_action: Show error +- id: call_page_recordings_api_list_recordings + source: + entity_id: page_recordings + file_path: app/recordings/page.tsx + endpoint_id: api_list_recordings + purpose: Display all recordings + trigger: onLoad + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Update state + error_action: Show error +- id: call_page_recording_detail_api_get_recording + source: + entity_id: page_recording_detail + file_path: app/recordings/[id]/page.tsx + endpoint_id: api_get_recording + purpose: Display recording details + trigger: onLoad + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Update state + error_action: Show error +- id: call_page_apps_api_list_apps + source: + entity_id: page_apps + file_path: app/apps/page.tsx + endpoint_id: api_list_apps + purpose: Display generated apps gallery + trigger: onLoad + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Update state + error_action: Show error +- id: call_page_app_detail_api_get_app + source: + entity_id: page_app_detail + file_path: app/apps/[id]/page.tsx + endpoint_id: api_get_app + purpose: Display app in iframe + trigger: onLoad + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Update state + error_action: Show error +- id: call_component_login_form_api_login_user + source: + entity_id: component_login_form + file_path: app/components/LoginForm.tsx + endpoint_id: api_login_user + purpose: Call api_login_user + trigger: onSubmit + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Handle response + error_action: Show error +- id: call_component_register_form_api_register_user + source: + entity_id: component_register_form + file_path: app/components/RegisterForm.tsx + endpoint_id: api_register_user + purpose: Call api_register_user + trigger: onSubmit + request_mapping: + from_props: [] + from_state: [] + from_form: [] + response_handling: + success_action: Handle response + error_action: Show error +backend_routes: +- id: route_post_auth_register + endpoint_id: api_register_user + file_path: app/api/auth/register/route.ts + export_name: POST + uses_models: + - model_user + uses_services: [] + must_validate: [] + must_authenticate: false + must_authorize: *id001 +- id: route_post_auth_login + endpoint_id: api_login_user + file_path: app/api/auth/login/route.ts + export_name: POST + uses_models: + - model_user + uses_services: [] + must_validate: [] + must_authenticate: false + must_authorize: *id002 +- id: route_post_auth_logout + endpoint_id: api_logout_user + file_path: app/api/auth/logout/route.ts + export_name: POST + uses_models: [] + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id003 +- id: route_get_auth_me + endpoint_id: api_get_current_user + file_path: app/api/auth/me/route.ts + export_name: GET + uses_models: + - model_user + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id004 +- id: route_get_recordings + endpoint_id: api_list_recordings + file_path: app/api/recordings/route.ts + export_name: GET + uses_models: + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id005 +- id: route_post_recordings + endpoint_id: api_create_recording + file_path: app/api/recordings/route.ts + export_name: POST + uses_models: + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id006 +- id: route_get_recordings_[id] + endpoint_id: api_get_recording + file_path: app/api/recordings/[id]/route.ts + export_name: GET + uses_models: + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id007 +- id: route_delete_recordings_[id] + endpoint_id: api_delete_recording + file_path: app/api/recordings/[id]/route.ts + export_name: DELETE + uses_models: + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id008 +- id: route_post_recordings_[id]_transcribe + endpoint_id: api_transcribe_recording + file_path: app/api/recordings/[id]/transcribe/route.ts + export_name: POST + uses_models: + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id009 +- id: route_post_recordings_[id]_summarize + endpoint_id: api_summarize_recording + file_path: app/api/recordings/[id]/summarize/route.ts + export_name: POST + uses_models: + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id010 +- id: route_get_apps + endpoint_id: api_list_apps + file_path: app/api/apps/route.ts + export_name: GET + uses_models: + - model_generated_app + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id011 +- id: route_post_apps_generate + endpoint_id: api_generate_app + file_path: app/api/apps/generate/route.ts + export_name: POST + uses_models: + - model_generated_app + - model_recording + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id012 +- id: route_get_apps_[id] + endpoint_id: api_get_app + file_path: app/api/apps/[id]/route.ts + export_name: GET + uses_models: + - model_generated_app + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id013 +- id: route_delete_apps_[id] + endpoint_id: api_delete_app + file_path: app/api/apps/[id]/route.ts + export_name: DELETE + uses_models: + - model_generated_app + uses_services: [] + must_validate: [] + must_authenticate: true + must_authorize: *id014 diff --git a/.workflow/versions/v001/dependency_graph.yml b/.workflow/versions/v001/dependency_graph.yml new file mode 100644 index 0000000..f687483 --- /dev/null +++ b/.workflow/versions/v001/dependency_graph.yml @@ -0,0 +1,712 @@ +dependency_graph: + design_version: 1 + workflow_version: v001 + generated_at: '2025-12-19T06:08:12.130212' + generator: validate_design.py + stats: + total_entities: 41 + total_layers: 5 + max_parallelism: 14 + critical_path_length: 5 + external_dependencies: 0 +external_dependencies: {} +layers: +- layer: 1 + name: Data Layer + description: Database models - no external dependencies + items: + - id: api_logout_user + type: api + name: Logout user + depends_on: [] + task_id: task_create_api_logout_user + agent: backend + complexity: medium + - id: component_app_card + type: component + name: AppCard + depends_on: [] + task_id: task_create_component_app_card + agent: frontend + complexity: medium + - id: component_app_iframe_viewer + type: component + name: AppIframeViewer + depends_on: [] + task_id: task_create_component_app_iframe_viewer + agent: frontend + complexity: medium + - id: component_audio_player + type: component + name: AudioPlayer + depends_on: [] + task_id: task_create_component_audio_player + agent: frontend + complexity: medium + - id: component_features + type: component + name: Features + depends_on: [] + task_id: task_create_component_features + agent: frontend + complexity: medium + - id: component_header + type: component + name: Header + depends_on: [] + task_id: task_create_component_header + agent: frontend + complexity: medium + - id: component_hero + type: component + name: Hero + depends_on: [] + task_id: task_create_component_hero + agent: frontend + complexity: medium + - id: component_record_button + type: component + name: RecordButton + depends_on: [] + task_id: task_create_component_record_button + agent: frontend + complexity: medium + - id: component_recording_card + type: component + name: RecordingCard + depends_on: [] + task_id: task_create_component_recording_card + agent: frontend + complexity: medium + - id: component_sidebar + type: component + name: Sidebar + depends_on: [] + task_id: task_create_component_sidebar + agent: frontend + complexity: medium + - id: component_summary_display + type: component + name: SummaryDisplay + depends_on: [] + task_id: task_create_component_summary_display + agent: frontend + complexity: medium + - id: component_transcript_viewer + type: component + name: TranscriptViewer + depends_on: [] + task_id: task_create_component_transcript_viewer + agent: frontend + complexity: medium + - id: component_wake_word_indicator + type: component + name: WakeWordIndicator + depends_on: [] + task_id: task_create_component_wake_word_indicator + agent: frontend + complexity: medium + - id: model_user + type: model + name: User + depends_on: [] + task_id: task_create_model_user + agent: backend + complexity: medium + requires_layers: [] + parallel_count: 14 +- layer: 2 + name: API Layer + description: REST endpoints - depend on models + items: + - id: api_get_current_user + type: api + name: Get current user + depends_on: + - model_user + task_id: task_create_api_get_current_user + agent: backend + complexity: medium + - id: api_login_user + type: api + name: Login user + depends_on: + - model_user + task_id: task_create_api_login_user + agent: backend + complexity: medium + - id: api_register_user + type: api + name: Register a new user + depends_on: + - model_user + task_id: task_create_api_register_user + agent: backend + complexity: medium + - id: component_app_gallery + type: component + name: AppGallery + depends_on: + - component_app_card + task_id: task_create_component_app_gallery + agent: frontend + complexity: medium + - id: component_recording_list + type: component + name: RecordingList + depends_on: + - component_recording_card + task_id: task_create_component_recording_list + agent: frontend + complexity: medium + - id: model_recording + type: model + name: Recording + depends_on: + - model_user + task_id: task_create_model_recording + agent: backend + complexity: medium + - id: page_home + type: page + name: Home + depends_on: + - component_features + - component_header + - component_hero + task_id: task_create_page_home + agent: frontend + complexity: medium + requires_layers: + - 1 + parallel_count: 7 +- layer: 3 + name: UI Layer + description: Pages and components - depend on APIs + items: + - id: api_create_recording + type: api + name: Create new recording + depends_on: + - model_recording + task_id: task_create_api_create_recording + agent: backend + complexity: medium + - id: api_delete_recording + type: api + name: Delete recording + depends_on: + - model_recording + task_id: task_create_api_delete_recording + agent: backend + complexity: medium + - id: api_get_recording + type: api + name: Get single recording + depends_on: + - model_recording + task_id: task_create_api_get_recording + agent: backend + complexity: medium + - id: api_list_recordings + type: api + name: List user recordings + depends_on: + - model_recording + task_id: task_create_api_list_recordings + agent: backend + complexity: medium + - id: api_transcribe_recording + type: api + name: Transcribe recording + depends_on: + - model_recording + task_id: task_create_api_transcribe_recording + agent: backend + complexity: medium + - id: component_login_form + type: component + name: LoginForm + depends_on: + - api_login_user + task_id: task_create_component_login_form + agent: frontend + complexity: medium + - id: component_register_form + type: component + name: RegisterForm + depends_on: + - api_register_user + task_id: task_create_component_register_form + agent: frontend + complexity: medium + - id: model_generated_app + type: model + name: GeneratedApp + depends_on: + - model_user + - model_recording + task_id: task_create_model_generated_app + agent: backend + complexity: medium + requires_layers: + - 1 + - 2 + parallel_count: 8 +- layer: 4 + name: Layer 4 + description: Entities with 3 levels of dependencies + items: + - id: api_delete_app + type: api + name: Delete generated app + depends_on: + - model_generated_app + task_id: task_create_api_delete_app + agent: backend + complexity: medium + - id: api_get_app + type: api + name: Get single generated app + depends_on: + - model_generated_app + task_id: task_create_api_get_app + agent: backend + complexity: medium + - id: api_list_apps + type: api + name: List generated apps + depends_on: + - model_generated_app + task_id: task_create_api_list_apps + agent: backend + complexity: medium + - id: api_summarize_recording + type: api + name: Summarize recording + depends_on: + - api_transcribe_recording + - model_recording + task_id: task_create_api_summarize_recording + agent: backend + complexity: medium + - id: page_dashboard + type: page + name: Dashboard + depends_on: + - component_recording_list + - api_list_recordings + - component_record_button + - api_get_current_user + - component_wake_word_indicator + - component_header + - component_sidebar + task_id: task_create_page_dashboard + agent: frontend + complexity: medium + - id: page_login + type: page + name: Login + depends_on: + - component_login_form + task_id: task_create_page_login + agent: frontend + complexity: medium + - id: page_recording_detail + type: page + name: Recording Detail + depends_on: + - component_transcript_viewer + - component_summary_display + - api_get_recording + - component_audio_player + - component_header + - component_sidebar + task_id: task_create_page_recording_detail + agent: frontend + complexity: medium + - id: page_recordings + type: page + name: Recordings + depends_on: + - api_list_recordings + - component_header + - component_recording_list + - component_recording_card + - component_sidebar + task_id: task_create_page_recordings + agent: frontend + complexity: medium + - id: page_register + type: page + name: Register + depends_on: + - component_register_form + task_id: task_create_page_register + agent: frontend + complexity: medium + requires_layers: + - 1 + - 2 + - 3 + parallel_count: 9 +- layer: 5 + name: Layer 5 + description: Entities with 4 levels of dependencies + items: + - id: api_generate_app + type: api + name: Generate app from recording + depends_on: + - api_summarize_recording + - model_generated_app + - model_recording + task_id: task_create_api_generate_app + agent: backend + complexity: medium + - id: page_app_detail + type: page + name: App Preview + depends_on: + - component_header + - component_app_iframe_viewer + - api_get_app + - component_sidebar + task_id: task_create_page_app_detail + agent: frontend + complexity: medium + - id: page_apps + type: page + name: Generated Apps + depends_on: + - api_list_apps + - component_app_card + - component_app_gallery + - component_header + - component_sidebar + task_id: task_create_page_apps + agent: frontend + complexity: medium + requires_layers: + - 1 + - 2 + - 3 + - 4 + parallel_count: 3 +dependency_map: + model_user: + type: model + layer: 1 + depends_on: [] + depended_by: + - model_recording + - api_get_current_user + - model_generated_app + - api_login_user + - api_register_user + model_recording: + type: model + layer: 2 + depends_on: + - model_user + depended_by: + - api_list_recordings + - api_create_recording + - api_transcribe_recording + - api_summarize_recording + - model_generated_app + - api_get_recording + - api_generate_app + - api_delete_recording + model_generated_app: + type: model + layer: 3 + depends_on: + - model_user + - model_recording + depended_by: + - api_delete_app + - api_get_app + - api_list_apps + - api_generate_app + api_register_user: + type: api + layer: 2 + depends_on: + - model_user + depended_by: + - component_register_form + api_login_user: + type: api + layer: 2 + depends_on: + - model_user + depended_by: + - component_login_form + api_logout_user: + type: api + layer: 1 + depends_on: [] + depended_by: [] + api_get_current_user: + type: api + layer: 2 + depends_on: + - model_user + depended_by: + - page_dashboard + api_list_recordings: + type: api + layer: 3 + depends_on: + - model_recording + depended_by: + - page_dashboard + - page_recordings + api_create_recording: + type: api + layer: 3 + depends_on: + - model_recording + depended_by: [] + api_get_recording: + type: api + layer: 3 + depends_on: + - model_recording + depended_by: + - page_recording_detail + api_delete_recording: + type: api + layer: 3 + depends_on: + - model_recording + depended_by: [] + api_transcribe_recording: + type: api + layer: 3 + depends_on: + - model_recording + depended_by: + - api_summarize_recording + api_summarize_recording: + type: api + layer: 4 + depends_on: + - api_transcribe_recording + - model_recording + depended_by: + - api_generate_app + api_list_apps: + type: api + layer: 4 + depends_on: + - model_generated_app + depended_by: + - page_apps + api_generate_app: + type: api + layer: 5 + depends_on: + - api_summarize_recording + - model_generated_app + - model_recording + depended_by: [] + api_get_app: + type: api + layer: 4 + depends_on: + - model_generated_app + depended_by: + - page_app_detail + api_delete_app: + type: api + layer: 4 + depends_on: + - model_generated_app + depended_by: [] + page_home: + type: page + layer: 2 + depends_on: + - component_features + - component_header + - component_hero + depended_by: [] + page_login: + type: page + layer: 4 + depends_on: + - component_login_form + depended_by: [] + page_register: + type: page + layer: 4 + depends_on: + - component_register_form + depended_by: [] + page_dashboard: + type: page + layer: 4 + depends_on: + - component_recording_list + - api_list_recordings + - component_record_button + - api_get_current_user + - component_wake_word_indicator + - component_header + - component_sidebar + depended_by: [] + page_recordings: + type: page + layer: 4 + depends_on: + - api_list_recordings + - component_header + - component_recording_list + - component_recording_card + - component_sidebar + depended_by: [] + page_recording_detail: + type: page + layer: 4 + depends_on: + - component_transcript_viewer + - component_summary_display + - api_get_recording + - component_audio_player + - component_header + - component_sidebar + depended_by: [] + page_apps: + type: page + layer: 5 + depends_on: + - api_list_apps + - component_app_card + - component_app_gallery + - component_header + - component_sidebar + depended_by: [] + page_app_detail: + type: page + layer: 5 + depends_on: + - component_header + - component_app_iframe_viewer + - api_get_app + - component_sidebar + depended_by: [] + component_header: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_app_detail + - page_home + - page_apps + - page_recordings + - page_recording_detail + - page_dashboard + component_sidebar: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_app_detail + - page_apps + - page_recordings + - page_recording_detail + - page_dashboard + component_hero: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_home + component_features: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_home + component_login_form: + type: component + layer: 3 + depends_on: + - api_login_user + depended_by: + - page_login + component_register_form: + type: component + layer: 3 + depends_on: + - api_register_user + depended_by: + - page_register + component_record_button: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_dashboard + component_wake_word_indicator: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_dashboard + component_recording_list: + type: component + layer: 2 + depends_on: + - component_recording_card + depended_by: + - page_dashboard + - page_recordings + component_recording_card: + type: component + layer: 1 + depends_on: [] + depended_by: + - component_recording_list + - page_recordings + component_audio_player: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_recording_detail + component_transcript_viewer: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_recording_detail + component_summary_display: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_recording_detail + component_app_gallery: + type: component + layer: 2 + depends_on: + - component_app_card + depended_by: + - page_apps + component_app_card: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_apps + - component_app_gallery + component_app_iframe_viewer: + type: component + layer: 1 + depends_on: [] + depended_by: + - page_app_detail +task_map: [] diff --git a/.workflow/versions/v001/design/design_document.yml b/.workflow/versions/v001/design/design_document.yml new file mode 100644 index 0000000..82dd6ed --- /dev/null +++ b/.workflow/versions/v001/design/design_document.yml @@ -0,0 +1,1665 @@ +# Design Document v001 +# Voice Recording App with AI-Powered Summarization and Automatic App Generation +# Created: 2024-12-19T06:00:00Z + +workflow_version: "v001" +feature: "Voice recording app with AI-powered summarization and automatic app generation" +created_at: "2024-12-19T06:00:00Z" +updated_at: "2024-12-19T06:00:00Z" +approved_at: null +status: draft +revision: 1 +revision_notes: "Initial design document" + +# ============================================================================ +# EXTERNAL DEPENDENCIES (Pre-existing entities) +# ============================================================================ +external_dependencies: + models: [] + api_endpoints: [] + components: [] + +# ============================================================================ +# LAYER 1: DATA MODELS +# ============================================================================ +data_models: + # USER MODEL + - id: model_user + name: User + description: "Application user account with email/password authentication" + table_name: users + fields: + - name: id + type: uuid + constraints: [primary_key] + description: "Unique user identifier" + - name: email + type: string + constraints: [unique, not_null, indexed] + description: "User email address for login" + - name: name + type: string + constraints: [not_null] + description: "User's display name" + - name: password_hash + type: string + constraints: [not_null] + description: "Bcrypt hashed password" + relations: [] + indexes: + - fields: [email] + unique: true + name: idx_users_email + timestamps: true + soft_delete: false + validations: + - field: email + rule: email + message: "Invalid email format" + - field: name + rule: "min:1" + message: "Name is required" + - field: password_hash + rule: "min:60" + message: "Invalid password hash" + + # RECORDING MODEL + - id: model_recording + name: Recording + description: "Voice recording with transcript and summary" + table_name: recordings + fields: + - name: id + type: uuid + constraints: [primary_key] + description: "Unique recording identifier" + - name: user_id + type: uuid + constraints: [foreign_key, not_null, indexed] + description: "Owner of this recording" + - name: title + type: string + constraints: [not_null] + description: "Recording title (auto-generated or user-edited)" + - name: audio_file_path + type: string + constraints: [not_null] + description: "Path to audio file in MinIO/S3" + - name: duration + type: integer + constraints: [not_null] + description: "Recording duration in seconds" + - name: transcript + type: text + constraints: [] + description: "Full transcript from Whisper STT" + - name: summary + type: text + constraints: [] + description: "AI-generated summary from Gemini" + - name: is_transcribing + type: boolean + constraints: [not_null, default] + default: false + description: "Whether currently transcribing" + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + indexes: + - fields: [user_id, created_at] + unique: false + name: idx_recordings_user_created + timestamps: true + soft_delete: false + validations: + - field: duration + rule: "min:1" + message: "Duration must be at least 1 second" + + # GENERATED APP MODEL + - id: model_generated_app + name: GeneratedApp + description: "AI-generated application from recording content" + table_name: generated_apps + fields: + - name: id + type: uuid + constraints: [primary_key] + description: "Unique app identifier" + - name: user_id + type: uuid + constraints: [foreign_key, not_null, indexed] + description: "Owner of this app" + - name: recording_id + type: uuid + constraints: [foreign_key, not_null, indexed] + description: "Source recording that triggered generation" + - name: title + type: string + constraints: [not_null] + description: "App title from AI analysis" + - name: description + type: text + constraints: [] + description: "App description" + - name: html_content + type: text + constraints: [not_null] + description: "Complete HTML/CSS/JS for iframe rendering" + - name: prd_content + type: text + constraints: [] + description: "Product Requirements Document (PRD) generated by AI" + - name: ui_ux_design + type: text + constraints: [] + description: "UI/UX design notes from AI" + - name: app_type + type: string + constraints: [] + description: "Type of app determined by AI (e.g., todo, calculator, form)" + - name: status + type: enum + enum_values: [generating, completed, failed] + constraints: [not_null, default] + default: generating + description: "Generation status" + relations: + - type: belongs_to + target: model_user + foreign_key: user_id + on_delete: cascade + - type: belongs_to + target: model_recording + foreign_key: recording_id + on_delete: cascade + indexes: + - fields: [user_id, created_at] + unique: false + name: idx_apps_user_created + - fields: [recording_id] + unique: false + name: idx_apps_recording + timestamps: true + soft_delete: false + validations: [] + +# ============================================================================ +# LAYER 2: API ENDPOINTS +# ============================================================================ +api_endpoints: + # ======================================== + # AUTHENTICATION ENDPOINTS + # ======================================== + - id: api_register_user + method: POST + path: /api/auth/register + summary: "Register a new user" + description: "Create a new user account with email and password" + tags: [auth] + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: email + type: string + required: true + validations: [email] + description: "User email address" + - name: name + type: string + required: true + validations: ["min:1", "max:100"] + description: "User display name" + - name: password + type: string + required: true + validations: ["min:8"] + description: "Password (min 8 characters)" + example: + email: "user@example.com" + name: "John Doe" + password: "securepass123" + responses: + - status: 201 + description: "User created successfully" + schema: + type: object + properties: + - name: id + type: uuid + - name: email + type: string + - name: name + type: string + - name: created_at + type: datetime + example: + id: "550e8400-e29b-41d4-a716-446655440000" + email: "user@example.com" + name: "John Doe" + created_at: "2024-12-19T06:00:00Z" + - status: 400 + description: "Validation error" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Invalid email or password too short" + - status: 409 + description: "Email already exists" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Email already registered" + depends_on_models: [model_user] + depends_on_apis: [] + auth: + required: false + roles: [] + rate_limit: + requests: 10 + window: "1h" + + - id: api_login_user + method: POST + path: /api/auth/login + summary: "Login user" + description: "Authenticate user with email and password, return session token" + tags: [auth] + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: email + type: string + required: true + validations: [email] + description: "User email" + - name: password + type: string + required: true + description: "User password" + example: + email: "user@example.com" + password: "securepass123" + responses: + - status: 200 + description: "Login successful" + schema: + type: object + properties: + - name: user + type: object + - name: token + type: string + example: + user: + id: "550e8400-e29b-41d4-a716-446655440000" + email: "user@example.com" + name: "John Doe" + token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + - status: 401 + description: "Invalid credentials" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Invalid email or password" + depends_on_models: [model_user] + depends_on_apis: [] + auth: + required: false + roles: [] + rate_limit: + requests: 20 + window: "1h" + + - id: api_logout_user + method: POST + path: /api/auth/logout + summary: "Logout user" + description: "Invalidate current session token" + tags: [auth] + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Logout successful" + schema: + type: object + properties: + - name: success + type: boolean + example: + success: true + depends_on_models: [] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_get_current_user + method: GET + path: /api/auth/me + summary: "Get current user" + description: "Get currently authenticated user information" + tags: [auth] + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "User data" + schema: + type: object + properties: + - name: id + type: uuid + - name: email + type: string + - name: name + type: string + - name: created_at + type: datetime + example: + id: "550e8400-e29b-41d4-a716-446655440000" + email: "user@example.com" + name: "John Doe" + created_at: "2024-12-19T06:00:00Z" + - status: 401 + description: "Not authenticated" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Not authenticated" + depends_on_models: [model_user] + depends_on_apis: [] + auth: + required: true + roles: [] + + # ======================================== + # RECORDING ENDPOINTS + # ======================================== + - id: api_list_recordings + method: GET + path: /api/recordings + summary: "List user recordings" + description: "Get all recordings for authenticated user, sorted by creation date" + tags: [recordings] + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: "Maximum number of recordings to return" + - name: offset + type: integer + required: false + default: 0 + description: "Number of recordings to skip" + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Recordings list" + schema: + type: object + properties: + - name: recordings + type: array + - name: total + type: integer + example: + recordings: + - id: "rec-123" + title: "Meeting Notes" + duration: 180 + created_at: "2024-12-19T06:00:00Z" + total: 42 + depends_on_models: [model_recording] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_create_recording + method: POST + path: /api/recordings + summary: "Create new recording" + description: "Upload audio file and create recording metadata" + tags: [recordings] + path_params: [] + query_params: [] + request_body: + content_type: multipart/form-data + schema: + type: object + properties: + - name: audio + type: file + required: true + description: "Audio file (webm, mp3, wav)" + - name: title + type: string + required: false + description: "Recording title (auto-generated if not provided)" + - name: duration + type: integer + required: true + description: "Duration in seconds" + responses: + - status: 201 + description: "Recording created" + schema: + type: object + properties: + - name: id + type: uuid + - name: title + type: string + - name: audio_file_path + type: string + - name: duration + type: integer + - name: created_at + type: datetime + example: + id: "rec-123" + title: "Recording 2024-12-19" + audio_file_path: "recordings/user-123/rec-123.webm" + duration: 180 + created_at: "2024-12-19T06:00:00Z" + - status: 400 + description: "Invalid audio file" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Invalid audio format" + depends_on_models: [model_recording] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_get_recording + method: GET + path: /api/recordings/[id] + summary: "Get single recording" + description: "Get recording details including transcript and summary" + tags: [recordings] + path_params: + - name: id + type: uuid + description: "Recording ID" + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Recording details" + schema: + type: object + properties: + - name: id + type: uuid + - name: title + type: string + - name: audio_file_path + type: string + - name: duration + type: integer + - name: transcript + type: string + - name: summary + type: string + - name: is_transcribing + type: boolean + - name: created_at + type: datetime + - status: 404 + description: "Recording not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Recording not found" + depends_on_models: [model_recording] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_delete_recording + method: DELETE + path: /api/recordings/[id] + summary: "Delete recording" + description: "Delete recording and associated audio file" + tags: [recordings] + path_params: + - name: id + type: uuid + description: "Recording ID" + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Recording deleted" + schema: + type: object + properties: + - name: success + type: boolean + example: + success: true + - status: 404 + description: "Recording not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Recording not found" + depends_on_models: [model_recording] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_transcribe_recording + method: POST + path: /api/recordings/[id]/transcribe + summary: "Transcribe recording" + description: "Run Whisper STT on audio file to generate transcript" + tags: [recordings, ai] + path_params: + - name: id + type: uuid + description: "Recording ID" + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Transcription started" + schema: + type: object + properties: + - name: recording_id + type: uuid + - name: is_transcribing + type: boolean + example: + recording_id: "rec-123" + is_transcribing: true + - status: 404 + description: "Recording not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Recording not found" + depends_on_models: [model_recording] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_summarize_recording + method: POST + path: /api/recordings/[id]/summarize + summary: "Summarize recording" + description: "Use Gemini to generate summary from transcript" + tags: [recordings, ai] + path_params: + - name: id + type: uuid + description: "Recording ID" + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Summary generated" + schema: + type: object + properties: + - name: recording_id + type: uuid + - name: summary + type: string + example: + recording_id: "rec-123" + summary: "Meeting discussion about Q1 goals..." + - status: 400 + description: "No transcript available" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Transcript not available" + - status: 404 + description: "Recording not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Recording not found" + depends_on_models: [model_recording] + depends_on_apis: [api_transcribe_recording] + auth: + required: true + roles: [] + + # ======================================== + # GENERATED APP ENDPOINTS + # ======================================== + - id: api_list_apps + method: GET + path: /api/apps + summary: "List generated apps" + description: "Get all apps generated for authenticated user" + tags: [apps] + path_params: [] + query_params: + - name: limit + type: integer + required: false + default: 50 + description: "Maximum number of apps to return" + - name: offset + type: integer + required: false + default: 0 + description: "Number of apps to skip" + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "Apps list" + schema: + type: object + properties: + - name: apps + type: array + - name: total + type: integer + example: + apps: + - id: "app-123" + title: "Todo App" + app_type: "todo" + status: "completed" + created_at: "2024-12-19T06:00:00Z" + total: 12 + depends_on_models: [model_generated_app] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_generate_app + method: POST + path: /api/apps/generate + summary: "Generate app from recording" + description: "Trigger AI app generation based on recording summary (wake word detection)" + tags: [apps, ai] + path_params: [] + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: + - name: recording_id + type: uuid + required: true + description: "Source recording ID" + example: + recording_id: "rec-123" + responses: + - status: 201 + description: "App generation started" + schema: + type: object + properties: + - name: id + type: uuid + - name: recording_id + type: uuid + - name: status + type: string + example: + id: "app-123" + recording_id: "rec-123" + status: "generating" + - status: 400 + description: "Recording has no summary" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Recording summary not available" + - status: 404 + description: "Recording not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "Recording not found" + depends_on_models: [model_generated_app, model_recording] + depends_on_apis: [api_summarize_recording] + auth: + required: true + roles: [] + + - id: api_get_app + method: GET + path: /api/apps/[id] + summary: "Get single generated app" + description: "Get app details including HTML content for iframe rendering" + tags: [apps] + path_params: + - name: id + type: uuid + description: "App ID" + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "App details" + schema: + type: object + properties: + - name: id + type: uuid + - name: recording_id + type: uuid + - name: title + type: string + - name: description + type: string + - name: html_content + type: string + - name: app_type + type: string + - name: status + type: string + - name: created_at + type: datetime + - status: 404 + description: "App not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "App not found" + depends_on_models: [model_generated_app] + depends_on_apis: [] + auth: + required: true + roles: [] + + - id: api_delete_app + method: DELETE + path: /api/apps/[id] + summary: "Delete generated app" + description: "Delete generated app (recording remains)" + tags: [apps] + path_params: + - name: id + type: uuid + description: "App ID" + query_params: [] + request_body: + content_type: application/json + schema: + type: object + properties: [] + responses: + - status: 200 + description: "App deleted" + schema: + type: object + properties: + - name: success + type: boolean + example: + success: true + - status: 404 + description: "App not found" + schema: + type: object + properties: + - name: error + type: string + example: + error: "App not found" + depends_on_models: [model_generated_app] + depends_on_apis: [] + auth: + required: true + roles: [] + +# ============================================================================ +# LAYER 3: PAGES +# ============================================================================ +pages: + - id: page_home + name: "Home" + path: / + layout: layout_public + data_needs: [] + components: [component_header, component_hero, component_features] + seo: + title: "NoteToApp - Voice Recording with AI Summarization" + description: "Record voice notes, get AI summaries, and generate apps automatically" + auth: + required: false + roles: [] + redirect: "" + state: + local: [] + global: [] + + - id: page_login + name: "Login" + path: /login + layout: layout_auth + data_needs: [] + components: [component_login_form] + seo: + title: "Login - NoteToApp" + description: "Login to your account" + auth: + required: false + roles: [] + redirect: "" + state: + local: [error, isLoading] + global: [] + + - id: page_register + name: "Register" + path: /register + layout: layout_auth + data_needs: [] + components: [component_register_form] + seo: + title: "Register - NoteToApp" + description: "Create a new account" + auth: + required: false + roles: [] + redirect: "" + state: + local: [error, isLoading] + global: [] + + - id: page_dashboard + name: "Dashboard" + path: /dashboard + layout: layout_app + data_needs: + - api_id: api_get_current_user + purpose: "Display user info" + on_load: true + - api_id: api_list_recordings + purpose: "Show recent recordings" + on_load: true + components: [component_header, component_sidebar, component_record_button, component_wake_word_indicator, component_recording_list] + seo: + title: "Dashboard - NoteToApp" + description: "Your voice recordings and generated apps" + auth: + required: true + roles: [] + redirect: /login + state: + local: [isRecording, isListening, currentRecording] + global: [user] + + - id: page_recordings + name: "Recordings" + path: /recordings + layout: layout_app + data_needs: + - api_id: api_list_recordings + purpose: "Display all recordings" + on_load: true + components: [component_header, component_sidebar, component_recording_list, component_recording_card] + seo: + title: "Recordings - NoteToApp" + description: "All your voice recordings" + auth: + required: true + roles: [] + redirect: /login + state: + local: [searchQuery, sortBy] + global: [user] + + - id: page_recording_detail + name: "Recording Detail" + path: /recordings/[id] + layout: layout_app + data_needs: + - api_id: api_get_recording + purpose: "Display recording details" + on_load: true + components: [component_header, component_sidebar, component_audio_player, component_transcript_viewer, component_summary_display] + seo: + title: "Recording Detail - NoteToApp" + description: "View recording transcript and summary" + auth: + required: true + roles: [] + redirect: /login + state: + local: [isPlaying, currentTime, isTranscribing] + global: [user] + + - id: page_apps + name: "Generated Apps" + path: /apps + layout: layout_app + data_needs: + - api_id: api_list_apps + purpose: "Display generated apps gallery" + on_load: true + components: [component_header, component_sidebar, component_app_gallery, component_app_card] + seo: + title: "Generated Apps - NoteToApp" + description: "Your AI-generated applications" + auth: + required: true + roles: [] + redirect: /login + state: + local: [filterType] + global: [user] + + - id: page_app_detail + name: "App Preview" + path: /apps/[id] + layout: layout_app + data_needs: + - api_id: api_get_app + purpose: "Display app in iframe" + on_load: true + components: [component_header, component_sidebar, component_app_iframe_viewer] + seo: + title: "App Preview - NoteToApp" + description: "View generated application" + auth: + required: true + roles: [] + redirect: /login + state: + local: [isLoading] + global: [user] + +# ============================================================================ +# LAYER 3: COMPONENTS +# ============================================================================ +components: + # ======================================== + # LAYOUT COMPONENTS + # ======================================== + - id: component_header + name: Header + props: + - name: user + type: User | null + required: false + default: null + description: "Current user for displaying name/avatar" + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: [default] + + - id: component_sidebar + name: Sidebar + props: + - name: activePath + type: string + required: true + description: "Current route path for highlighting" + events: + - name: onNavigate + payload: "string" + description: "Fired when navigation item clicked" + uses_apis: [] + uses_components: [] + internal_state: [isCollapsed] + variants: [default] + + - id: component_hero + name: Hero + props: [] + events: + - name: onGetStarted + payload: "void" + description: "Fired when CTA button clicked" + uses_apis: [] + uses_components: [] + internal_state: [] + variants: [default] + + - id: component_features + name: Features + props: [] + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: [default] + + # ======================================== + # AUTHENTICATION COMPONENTS + # ======================================== + - id: component_login_form + name: LoginForm + props: [] + events: + - name: onSuccess + payload: "User" + description: "Fired when login successful" + - name: onError + payload: "string" + description: "Fired when login fails" + uses_apis: [api_login_user] + uses_components: [] + internal_state: [email, password, isLoading, error] + variants: [default] + + - id: component_register_form + name: RegisterForm + props: [] + events: + - name: onSuccess + payload: "User" + description: "Fired when registration successful" + - name: onError + payload: "string" + description: "Fired when registration fails" + uses_apis: [api_register_user] + uses_components: [] + internal_state: [email, name, password, confirmPassword, isLoading, error] + variants: [default] + + # ======================================== + # RECORDING COMPONENTS + # ======================================== + - id: component_record_button + name: RecordButton + props: + - name: isRecording + type: boolean + required: true + description: "Whether currently recording" + - name: isTranscribing + type: boolean + required: false + default: false + description: "Whether transcribing in real-time" + events: + - name: onStartRecording + payload: "void" + description: "Fired when recording starts" + - name: onStopRecording + payload: "Blob" + description: "Fired when recording stops, includes audio blob" + uses_apis: [] + uses_components: [] + internal_state: [mediaRecorder, audioChunks] + variants: [default, large] + + - id: component_wake_word_indicator + name: WakeWordIndicator + props: + - name: isListening + type: boolean + required: true + description: "Whether wake word detection is active" + - name: wakeWord + type: string + required: true + description: "The wake word phrase" + events: + - name: onWakeWordDetected + payload: "void" + description: "Fired when wake word is detected" + uses_apis: [] + uses_components: [] + internal_state: [lastDetectionTime] + variants: [default] + + - id: component_recording_list + name: RecordingList + props: + - name: recordings + type: "Recording[]" + required: true + description: "Array of recordings to display" + - name: isLoading + type: boolean + required: false + default: false + description: "Whether recordings are being loaded" + events: + - name: onSelectRecording + payload: "string" + description: "Fired when recording is clicked, payload is recording ID" + - name: onDeleteRecording + payload: "string" + description: "Fired when delete confirmed, payload is recording ID" + uses_apis: [] + uses_components: [component_recording_card] + internal_state: [] + variants: [default, compact] + + - id: component_recording_card + name: RecordingCard + props: + - name: recording + type: Recording + required: true + description: "Recording to display" + - name: showActions + type: boolean + required: false + default: true + description: "Show edit/delete buttons" + events: + - name: onClick + payload: "string" + description: "Fired when card clicked, payload is recording ID" + - name: onDelete + payload: "string" + description: "Fired when delete confirmed, payload is recording ID" + uses_apis: [] + uses_components: [] + internal_state: [isDeleting] + variants: [default, compact] + + - id: component_audio_player + name: AudioPlayer + props: + - name: audioUrl + type: string + required: true + description: "URL to audio file" + - name: duration + type: integer + required: true + description: "Duration in seconds" + events: + - name: onPlayPause + payload: "boolean" + description: "Fired when play/pause toggled, payload is isPlaying" + - name: onSeek + payload: "number" + description: "Fired when user seeks, payload is new time in seconds" + uses_apis: [] + uses_components: [] + internal_state: [isPlaying, currentTime, audioElement] + variants: [default] + + - id: component_transcript_viewer + name: TranscriptViewer + props: + - name: transcript + type: string + required: true + description: "Transcript text to display" + - name: isLive + type: boolean + required: false + default: false + description: "Whether showing live transcription" + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: [default, live] + + - id: component_summary_display + name: SummaryDisplay + props: + - name: summary + type: string + required: true + description: "AI-generated summary text" + - name: isLoading + type: boolean + required: false + default: false + description: "Whether summary is being generated" + events: [] + uses_apis: [] + uses_components: [] + internal_state: [] + variants: [default] + + # ======================================== + # GENERATED APP COMPONENTS + # ======================================== + - id: component_app_gallery + name: AppGallery + props: + - name: apps + type: "GeneratedApp[]" + required: true + description: "Array of generated apps to display" + - name: isLoading + type: boolean + required: false + default: false + description: "Whether apps are being loaded" + events: + - name: onSelectApp + payload: "string" + description: "Fired when app is clicked, payload is app ID" + uses_apis: [] + uses_components: [component_app_card] + internal_state: [] + variants: [grid, list] + + - id: component_app_card + name: AppCard + props: + - name: app + type: GeneratedApp + required: true + description: "Generated app to display" + - name: showActions + type: boolean + required: false + default: true + description: "Show delete button" + events: + - name: onClick + payload: "string" + description: "Fired when card clicked, payload is app ID" + - name: onDelete + payload: "string" + description: "Fired when delete confirmed, payload is app ID" + uses_apis: [] + uses_components: [] + internal_state: [isDeleting] + variants: [default, compact] + + - id: component_app_iframe_viewer + name: AppIframeViewer + props: + - name: htmlContent + type: string + required: true + description: "HTML content to render in iframe" + - name: title + type: string + required: true + description: "App title for accessibility" + events: + - name: onLoadComplete + payload: "void" + description: "Fired when iframe finishes loading" + uses_apis: [] + uses_components: [] + internal_state: [isLoading, iframeRef] + variants: [default, fullscreen] + +# ============================================================================ +# DEPENDENCY GRAPH +# ============================================================================ +dependency_graph: + layers: + - layer: 1 + name: "Data Models" + description: "Database schema - no dependencies" + items: + - id: model_user + type: model + dependencies: [] + - id: model_recording + type: model + dependencies: [] + - id: model_generated_app + type: model + dependencies: [] + + - layer: 2 + name: "API Endpoints" + description: "Backend APIs - depend on models" + items: + - id: api_register_user + type: api + dependencies: [model_user] + - id: api_login_user + type: api + dependencies: [model_user] + - id: api_logout_user + type: api + dependencies: [] + - id: api_get_current_user + type: api + dependencies: [model_user] + - id: api_list_recordings + type: api + dependencies: [model_recording] + - id: api_create_recording + type: api + dependencies: [model_recording] + - id: api_get_recording + type: api + dependencies: [model_recording] + - id: api_delete_recording + type: api + dependencies: [model_recording] + - id: api_transcribe_recording + type: api + dependencies: [model_recording] + - id: api_summarize_recording + type: api + dependencies: [model_recording] + - id: api_list_apps + type: api + dependencies: [model_generated_app] + - id: api_generate_app + type: api + dependencies: [model_generated_app, model_recording] + - id: api_get_app + type: api + dependencies: [model_generated_app] + - id: api_delete_app + type: api + dependencies: [model_generated_app] + + - layer: 3 + name: "UI Components & Pages" + description: "Frontend - depend on APIs" + items: + # Components + - id: component_header + type: component + dependencies: [] + - id: component_sidebar + type: component + dependencies: [] + - id: component_hero + type: component + dependencies: [] + - id: component_features + type: component + dependencies: [] + - id: component_login_form + type: component + dependencies: [api_login_user] + - id: component_register_form + type: component + dependencies: [api_register_user] + - id: component_record_button + type: component + dependencies: [] + - id: component_wake_word_indicator + type: component + dependencies: [] + - id: component_recording_list + type: component + dependencies: [component_recording_card] + - id: component_recording_card + type: component + dependencies: [] + - id: component_audio_player + type: component + dependencies: [] + - id: component_transcript_viewer + type: component + dependencies: [] + - id: component_summary_display + type: component + dependencies: [] + - id: component_app_gallery + type: component + dependencies: [component_app_card] + - id: component_app_card + type: component + dependencies: [] + - id: component_app_iframe_viewer + type: component + dependencies: [] + + # Pages + - id: page_home + type: page + dependencies: [component_header, component_hero, component_features] + - id: page_login + type: page + dependencies: [component_login_form] + - id: page_register + type: page + dependencies: [component_register_form] + - id: page_dashboard + type: page + dependencies: [api_get_current_user, api_list_recordings, component_header, component_sidebar, component_record_button, component_wake_word_indicator, component_recording_list] + - id: page_recordings + type: page + dependencies: [api_list_recordings, component_header, component_sidebar, component_recording_list, component_recording_card] + - id: page_recording_detail + type: page + dependencies: [api_get_recording, component_header, component_sidebar, component_audio_player, component_transcript_viewer, component_summary_display] + - id: page_apps + type: page + dependencies: [api_list_apps, component_header, component_sidebar, component_app_gallery, component_app_card] + - id: page_app_detail + type: page + dependencies: [api_get_app, component_header, component_sidebar, component_app_iframe_viewer] + + dependency_map: + # Models + model_user: + depends_on: [] + depended_by: [api_register_user, api_login_user, api_get_current_user] + model_recording: + depends_on: [] + depended_by: [api_list_recordings, api_create_recording, api_get_recording, api_delete_recording, api_transcribe_recording, api_summarize_recording, api_generate_app] + model_generated_app: + depends_on: [] + depended_by: [api_list_apps, api_generate_app, api_get_app, api_delete_app] + + # APIs - Auth + api_register_user: + depends_on: [model_user] + depended_by: [component_register_form] + api_login_user: + depends_on: [model_user] + depended_by: [component_login_form] + api_logout_user: + depends_on: [] + depended_by: [] + api_get_current_user: + depends_on: [model_user] + depended_by: [page_dashboard] + + # APIs - Recordings + api_list_recordings: + depends_on: [model_recording] + depended_by: [page_dashboard, page_recordings] + api_create_recording: + depends_on: [model_recording] + depended_by: [] + api_get_recording: + depends_on: [model_recording] + depended_by: [page_recording_detail] + api_delete_recording: + depends_on: [model_recording] + depended_by: [] + api_transcribe_recording: + depends_on: [model_recording] + depended_by: [] + api_summarize_recording: + depends_on: [model_recording] + depended_by: [] + + # APIs - Apps + api_list_apps: + depends_on: [model_generated_app] + depended_by: [page_apps] + api_generate_app: + depends_on: [model_generated_app, model_recording] + depended_by: [] + api_get_app: + depends_on: [model_generated_app] + depended_by: [page_app_detail] + api_delete_app: + depends_on: [model_generated_app] + depended_by: [] + + # Components + component_header: + depends_on: [] + depended_by: [page_home, page_dashboard, page_recordings, page_recording_detail, page_apps, page_app_detail] + component_sidebar: + depends_on: [] + depended_by: [page_dashboard, page_recordings, page_recording_detail, page_apps, page_app_detail] + component_hero: + depends_on: [] + depended_by: [page_home] + component_features: + depends_on: [] + depended_by: [page_home] + component_login_form: + depends_on: [api_login_user] + depended_by: [page_login] + component_register_form: + depends_on: [api_register_user] + depended_by: [page_register] + component_record_button: + depends_on: [] + depended_by: [page_dashboard] + component_wake_word_indicator: + depends_on: [] + depended_by: [page_dashboard] + component_recording_list: + depends_on: [component_recording_card] + depended_by: [page_dashboard, page_recordings] + component_recording_card: + depends_on: [] + depended_by: [component_recording_list, page_recordings] + component_audio_player: + depends_on: [] + depended_by: [page_recording_detail] + component_transcript_viewer: + depends_on: [] + depended_by: [page_recording_detail] + component_summary_display: + depends_on: [] + depended_by: [page_recording_detail] + component_app_gallery: + depends_on: [component_app_card] + depended_by: [page_apps] + component_app_card: + depends_on: [] + depended_by: [component_app_gallery, page_apps] + component_app_iframe_viewer: + depends_on: [] + depended_by: [page_app_detail] + + # Pages + page_home: + depends_on: [component_header, component_hero, component_features] + depended_by: [] + page_login: + depends_on: [component_login_form] + depended_by: [] + page_register: + depends_on: [component_register_form] + depended_by: [] + page_dashboard: + depends_on: [api_get_current_user, api_list_recordings, component_header, component_sidebar, component_record_button, component_wake_word_indicator, component_recording_list] + depended_by: [] + page_recordings: + depends_on: [api_list_recordings, component_header, component_sidebar, component_recording_list, component_recording_card] + depended_by: [] + page_recording_detail: + depends_on: [api_get_recording, component_header, component_sidebar, component_audio_player, component_transcript_viewer, component_summary_display] + depended_by: [] + page_apps: + depends_on: [api_list_apps, component_header, component_sidebar, component_app_gallery, component_app_card] + depended_by: [] + page_app_detail: + depends_on: [api_get_app, component_header, component_sidebar, component_app_iframe_viewer] + depended_by: [] diff --git a/.workflow/versions/v001/relations.yml b/.workflow/versions/v001/relations.yml new file mode 100644 index 0000000..5934582 --- /dev/null +++ b/.workflow/versions/v001/relations.yml @@ -0,0 +1,1175 @@ +version: '1.0' +generated_at: '2025-12-19T06:08:20.694285' +source: .workflow/versions/v001/design/design_document.yml +entities: + database: + - id: model_user + type: database + name: User + file_path: prisma/schema.prisma + layer: 1 + depends_on: [] + used_by: + - model_recording + - model_generated_app + - api_register_user + - api_login_user + - api_get_current_user + status: pending + - id: model_recording + type: database + name: Recording + file_path: prisma/schema.prisma + layer: 1 + depends_on: + - model_user + used_by: + - model_generated_app + - api_list_recordings + - api_create_recording + - api_get_recording + - api_delete_recording + - api_transcribe_recording + - api_summarize_recording + - api_generate_app + status: pending + - id: model_generated_app + type: database + name: GeneratedApp + file_path: prisma/schema.prisma + layer: 1 + depends_on: + - model_user + - model_recording + used_by: + - api_list_apps + - api_generate_app + - api_get_app + - api_delete_app + status: pending + api: + - id: api_register_user + type: api + name: Register a new user + file_path: app/api/auth/register/route.ts + layer: 2 + depends_on: + - model_user + used_by: + - component_register_form + status: pending + - id: api_login_user + type: api + name: Login user + file_path: app/api/auth/login/route.ts + layer: 2 + depends_on: + - model_user + used_by: + - component_login_form + status: pending + - id: api_logout_user + type: api + name: Logout user + file_path: app/api/auth/logout/route.ts + layer: 2 + depends_on: [] + used_by: [] + status: pending + - id: api_get_current_user + type: api + name: Get current user + file_path: app/api/auth/me/route.ts + layer: 2 + depends_on: + - model_user + used_by: + - page_dashboard + status: pending + - id: api_list_recordings + type: api + name: List user recordings + file_path: app/api/recordings/route.ts + layer: 2 + depends_on: + - model_recording + used_by: + - page_dashboard + - page_recordings + status: pending + - id: api_create_recording + type: api + name: Create new recording + file_path: app/api/recordings/route.ts + layer: 2 + depends_on: + - model_recording + used_by: [] + status: pending + - id: api_get_recording + type: api + name: Get single recording + file_path: app/api/recordings/[id]/route.ts + layer: 2 + depends_on: + - model_recording + used_by: + - page_recording_detail + status: pending + - id: api_delete_recording + type: api + name: Delete recording + file_path: app/api/recordings/[id]/route.ts + layer: 2 + depends_on: + - model_recording + used_by: [] + status: pending + - id: api_transcribe_recording + type: api + name: Transcribe recording + file_path: app/api/recordings/[id]/transcribe/route.ts + layer: 2 + depends_on: + - model_recording + used_by: + - api_summarize_recording + status: pending + - id: api_summarize_recording + type: api + name: Summarize recording + file_path: app/api/recordings/[id]/summarize/route.ts + layer: 2 + depends_on: + - model_recording + - api_transcribe_recording + used_by: + - api_generate_app + status: pending + - id: api_list_apps + type: api + name: List generated apps + file_path: app/api/apps/route.ts + layer: 2 + depends_on: + - model_generated_app + used_by: + - page_apps + status: pending + - id: api_generate_app + type: api + name: Generate app from recording + file_path: app/api/apps/generate/route.ts + layer: 2 + depends_on: + - model_generated_app + - model_recording + - api_summarize_recording + used_by: [] + status: pending + - id: api_get_app + type: api + name: Get single generated app + file_path: app/api/apps/[id]/route.ts + layer: 2 + depends_on: + - model_generated_app + used_by: + - page_app_detail + status: pending + - id: api_delete_app + type: api + name: Delete generated app + file_path: app/api/apps/[id]/route.ts + layer: 2 + depends_on: + - model_generated_app + used_by: [] + status: pending + component: + - id: component_header + type: component + name: Header + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_home + - page_dashboard + - page_recordings + - page_recording_detail + - page_apps + - page_app_detail + status: pending + - id: component_sidebar + type: component + name: Sidebar + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_dashboard + - page_recordings + - page_recording_detail + - page_apps + - page_app_detail + status: pending + - id: component_hero + type: component + name: Hero + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_home + status: pending + - id: component_features + type: component + name: Features + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_home + status: pending + - id: component_login_form + type: component + name: LoginForm + file_path: '' + layer: 3 + depends_on: + - api_login_user + used_by: + - page_login + status: pending + - id: component_register_form + type: component + name: RegisterForm + file_path: '' + layer: 3 + depends_on: + - api_register_user + used_by: + - page_register + status: pending + - id: component_record_button + type: component + name: RecordButton + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_dashboard + status: pending + - id: component_wake_word_indicator + type: component + name: WakeWordIndicator + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_dashboard + status: pending + - id: component_recording_list + type: component + name: RecordingList + file_path: '' + layer: 3 + depends_on: + - component_recording_card + used_by: + - page_dashboard + - page_recordings + status: pending + - id: component_recording_card + type: component + name: RecordingCard + file_path: '' + layer: 3 + depends_on: [] + used_by: + - component_recording_list + - page_recordings + status: pending + - id: component_audio_player + type: component + name: AudioPlayer + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_recording_detail + status: pending + - id: component_transcript_viewer + type: component + name: TranscriptViewer + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_recording_detail + status: pending + - id: component_summary_display + type: component + name: SummaryDisplay + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_recording_detail + status: pending + - id: component_app_gallery + type: component + name: AppGallery + file_path: '' + layer: 3 + depends_on: + - component_app_card + used_by: + - page_apps + status: pending + - id: component_app_card + type: component + name: AppCard + file_path: '' + layer: 3 + depends_on: [] + used_by: + - component_app_gallery + - page_apps + status: pending + - id: component_app_iframe_viewer + type: component + name: AppIframeViewer + file_path: '' + layer: 3 + depends_on: [] + used_by: + - page_app_detail + status: pending + page: + - id: page_home + type: page + name: Home + file_path: app//page.tsx + layer: 4 + depends_on: + - component_header + - component_hero + - component_features + used_by: [] + status: pending + - id: page_login + type: page + name: Login + file_path: app/login/page.tsx + layer: 4 + depends_on: + - component_login_form + used_by: [] + status: pending + - id: page_register + type: page + name: Register + file_path: app/register/page.tsx + layer: 4 + depends_on: + - component_register_form + used_by: [] + status: pending + - id: page_dashboard + type: page + name: Dashboard + file_path: app/dashboard/page.tsx + layer: 4 + depends_on: + - api_get_current_user + - api_list_recordings + - component_header + - component_sidebar + - component_record_button + - component_wake_word_indicator + - component_recording_list + used_by: [] + status: pending + - id: page_recordings + type: page + name: Recordings + file_path: app/recordings/page.tsx + layer: 4 + depends_on: + - api_list_recordings + - component_header + - component_sidebar + - component_recording_list + - component_recording_card + used_by: [] + status: pending + - id: page_recording_detail + type: page + name: Recording Detail + file_path: app/recordings/[id]/page.tsx + layer: 4 + depends_on: + - api_get_recording + - component_header + - component_sidebar + - component_audio_player + - component_transcript_viewer + - component_summary_display + used_by: [] + status: pending + - id: page_apps + type: page + name: Generated Apps + file_path: app/apps/page.tsx + layer: 4 + depends_on: + - api_list_apps + - component_header + - component_sidebar + - component_app_gallery + - component_app_card + used_by: [] + status: pending + - id: page_app_detail + type: page + name: App Preview + file_path: app/apps/[id]/page.tsx + layer: 4 + depends_on: + - api_get_app + - component_header + - component_sidebar + - component_app_iframe_viewer + used_by: [] + status: pending +relations: +- source: model_recording + target: model_user + type: references +- source: model_generated_app + target: model_user + type: references +- source: model_generated_app + target: model_recording + type: references +- source: api_register_user + target: model_user + type: queries +- source: api_login_user + target: model_user + type: queries +- source: api_get_current_user + target: model_user + type: queries +- source: api_list_recordings + target: model_recording + type: queries +- source: api_create_recording + target: model_recording + type: queries +- source: api_get_recording + target: model_recording + type: queries +- source: api_delete_recording + target: model_recording + type: queries +- source: api_transcribe_recording + target: model_recording + type: queries +- source: api_summarize_recording + target: model_recording + type: queries +- source: api_summarize_recording + target: api_transcribe_recording + type: calls +- source: api_list_apps + target: model_generated_app + type: queries +- source: api_generate_app + target: model_generated_app + type: queries +- source: api_generate_app + target: model_recording + type: queries +- source: api_generate_app + target: api_summarize_recording + type: calls +- source: api_get_app + target: model_generated_app + type: queries +- source: api_delete_app + target: model_generated_app + type: queries +- source: component_login_form + target: api_login_user + type: calls +- source: component_register_form + target: api_register_user + type: calls +- source: component_recording_list + target: component_recording_card + type: imports +- source: component_app_gallery + target: component_app_card + type: imports +- source: page_home + target: component_header + type: renders +- source: page_home + target: component_hero + type: renders +- source: page_home + target: component_features + type: renders +- source: page_login + target: component_login_form + type: renders +- source: page_register + target: component_register_form + type: renders +- source: page_dashboard + target: api_get_current_user + type: calls +- source: page_dashboard + target: api_list_recordings + type: calls +- source: page_dashboard + target: component_header + type: renders +- source: page_dashboard + target: component_sidebar + type: renders +- source: page_dashboard + target: component_record_button + type: renders +- source: page_dashboard + target: component_wake_word_indicator + type: renders +- source: page_dashboard + target: component_recording_list + type: renders +- source: page_recordings + target: api_list_recordings + type: calls +- source: page_recordings + target: component_header + type: renders +- source: page_recordings + target: component_sidebar + type: renders +- source: page_recordings + target: component_recording_list + type: renders +- source: page_recordings + target: component_recording_card + type: renders +- source: page_recording_detail + target: api_get_recording + type: calls +- source: page_recording_detail + target: component_header + type: renders +- source: page_recording_detail + target: component_sidebar + type: renders +- source: page_recording_detail + target: component_audio_player + type: renders +- source: page_recording_detail + target: component_transcript_viewer + type: renders +- source: page_recording_detail + target: component_summary_display + type: renders +- source: page_apps + target: api_list_apps + type: calls +- source: page_apps + target: component_header + type: renders +- source: page_apps + target: component_sidebar + type: renders +- source: page_apps + target: component_app_gallery + type: renders +- source: page_apps + target: component_app_card + type: renders +- source: page_app_detail + target: api_get_app + type: calls +- source: page_app_detail + target: component_header + type: renders +- source: page_app_detail + target: component_sidebar + type: renders +- source: page_app_detail + target: component_app_iframe_viewer + type: renders +- source: model_recording + target: model_user + type: references +- source: model_generated_app + target: model_user + type: references +- source: model_generated_app + target: model_recording + type: references +- source: api_register_user + target: model_user + type: queries +- source: api_login_user + target: model_user + type: queries +- source: api_get_current_user + target: model_user + type: queries +- source: api_list_recordings + target: model_recording + type: queries +- source: api_create_recording + target: model_recording + type: queries +- source: api_get_recording + target: model_recording + type: queries +- source: api_delete_recording + target: model_recording + type: queries +- source: api_transcribe_recording + target: model_recording + type: queries +- source: api_summarize_recording + target: model_recording + type: queries +- source: api_summarize_recording + target: api_transcribe_recording + type: calls +- source: api_list_apps + target: model_generated_app + type: queries +- source: api_generate_app + target: model_generated_app + type: queries +- source: api_generate_app + target: model_recording + type: queries +- source: api_generate_app + target: api_summarize_recording + type: calls +- source: api_get_app + target: model_generated_app + type: queries +- source: api_delete_app + target: model_generated_app + type: queries +- source: component_login_form + target: api_login_user + type: calls +- source: component_register_form + target: api_register_user + type: calls +- source: component_recording_list + target: component_recording_card + type: imports +- source: component_app_gallery + target: component_app_card + type: imports +- source: page_home + target: component_header + type: renders +- source: page_home + target: component_hero + type: renders +- source: page_home + target: component_features + type: renders +- source: page_login + target: component_login_form + type: renders +- source: page_register + target: component_register_form + type: renders +- source: page_dashboard + target: api_get_current_user + type: calls +- source: page_dashboard + target: api_list_recordings + type: calls +- source: page_dashboard + target: component_header + type: renders +- source: page_dashboard + target: component_sidebar + type: renders +- source: page_dashboard + target: component_record_button + type: renders +- source: page_dashboard + target: component_wake_word_indicator + type: renders +- source: page_dashboard + target: component_recording_list + type: renders +- source: page_recordings + target: api_list_recordings + type: calls +- source: page_recordings + target: component_header + type: renders +- source: page_recordings + target: component_sidebar + type: renders +- source: page_recordings + target: component_recording_list + type: renders +- source: page_recordings + target: component_recording_card + type: renders +- source: page_recording_detail + target: api_get_recording + type: calls +- source: page_recording_detail + target: component_header + type: renders +- source: page_recording_detail + target: component_sidebar + type: renders +- source: page_recording_detail + target: component_audio_player + type: renders +- source: page_recording_detail + target: component_transcript_viewer + type: renders +- source: page_recording_detail + target: component_summary_display + type: renders +- source: page_apps + target: api_list_apps + type: calls +- source: page_apps + target: component_header + type: renders +- source: page_apps + target: component_sidebar + type: renders +- source: page_apps + target: component_app_gallery + type: renders +- source: page_apps + target: component_app_card + type: renders +- source: page_app_detail + target: api_get_app + type: calls +- source: page_app_detail + target: component_header + type: renders +- source: page_app_detail + target: component_sidebar + type: renders +- source: page_app_detail + target: component_app_iframe_viewer + type: renders +dependency_chains: + model_user: [] + model_recording: + - model_user + model_generated_app: + - model_user + - model_recording + api_register_user: + - model_user + api_login_user: + - model_user + api_logout_user: [] + api_get_current_user: + - model_user + api_list_recordings: + - model_user + - model_recording + api_create_recording: + - model_user + - model_recording + api_get_recording: + - model_user + - model_recording + api_delete_recording: + - model_user + - model_recording + api_transcribe_recording: + - model_user + - model_recording + api_summarize_recording: + - model_user + - model_recording + - api_transcribe_recording + api_list_apps: + - model_user + - model_recording + - model_generated_app + api_generate_app: + - model_user + - model_recording + - model_generated_app + - api_transcribe_recording + - api_summarize_recording + api_get_app: + - model_user + - model_recording + - model_generated_app + api_delete_app: + - model_user + - model_recording + - model_generated_app + component_header: [] + component_sidebar: [] + component_hero: [] + component_features: [] + component_login_form: + - model_user + - api_login_user + component_register_form: + - model_user + - api_register_user + component_record_button: [] + component_wake_word_indicator: [] + component_recording_list: + - component_recording_card + component_recording_card: [] + component_audio_player: [] + component_transcript_viewer: [] + component_summary_display: [] + component_app_gallery: + - component_app_card + component_app_card: [] + component_app_iframe_viewer: [] + page_home: + - component_header + - component_hero + - component_features + page_login: + - model_user + - api_login_user + - component_login_form + page_register: + - model_user + - api_register_user + - component_register_form + page_dashboard: + - model_user + - api_get_current_user + - model_recording + - api_list_recordings + - component_header + - component_sidebar + - component_record_button + - component_wake_word_indicator + - component_recording_card + - component_recording_list + page_recordings: + - model_user + - model_recording + - api_list_recordings + - component_header + - component_sidebar + - component_recording_card + - component_recording_list + page_recording_detail: + - model_user + - model_recording + - api_get_recording + - component_header + - component_sidebar + - component_audio_player + - component_transcript_viewer + - component_summary_display + page_apps: + - model_user + - model_recording + - model_generated_app + - api_list_apps + - component_header + - component_sidebar + - component_app_card + - component_app_gallery + page_app_detail: + - model_user + - model_recording + - model_generated_app + - api_get_app + - component_header + - component_sidebar + - component_app_iframe_viewer +impact_chains: + model_user: + - model_recording + - model_generated_app + - api_list_apps + - page_apps + - api_generate_app + - api_get_app + - page_app_detail + - api_delete_app + - api_list_recordings + - page_dashboard + - page_recordings + - api_create_recording + - api_get_recording + - page_recording_detail + - api_delete_recording + - api_transcribe_recording + - api_summarize_recording + - api_register_user + - component_register_form + - page_register + - api_login_user + - component_login_form + - page_login + - api_get_current_user + model_recording: + - model_generated_app + - api_list_apps + - page_apps + - api_generate_app + - api_get_app + - page_app_detail + - api_delete_app + - api_list_recordings + - page_dashboard + - page_recordings + - api_create_recording + - api_get_recording + - page_recording_detail + - api_delete_recording + - api_transcribe_recording + - api_summarize_recording + model_generated_app: + - api_list_apps + - page_apps + - api_generate_app + - api_get_app + - page_app_detail + - api_delete_app + api_register_user: + - component_register_form + - page_register + api_login_user: + - component_login_form + - page_login + api_logout_user: [] + api_get_current_user: + - page_dashboard + api_list_recordings: + - page_dashboard + - page_recordings + api_create_recording: [] + api_get_recording: + - page_recording_detail + api_delete_recording: [] + api_transcribe_recording: + - api_summarize_recording + - api_generate_app + api_summarize_recording: + - api_generate_app + api_list_apps: + - page_apps + api_generate_app: [] + api_get_app: + - page_app_detail + api_delete_app: [] + component_header: + - page_home + - page_dashboard + - page_recordings + - page_recording_detail + - page_apps + - page_app_detail + component_sidebar: + - page_dashboard + - page_recordings + - page_recording_detail + - page_apps + - page_app_detail + component_hero: + - page_home + component_features: + - page_home + component_login_form: + - page_login + component_register_form: + - page_register + component_record_button: + - page_dashboard + component_wake_word_indicator: + - page_dashboard + component_recording_list: + - page_dashboard + - page_recordings + component_recording_card: + - component_recording_list + - page_dashboard + - page_recordings + component_audio_player: + - page_recording_detail + component_transcript_viewer: + - page_recording_detail + component_summary_display: + - page_recording_detail + component_app_gallery: + - page_apps + component_app_card: + - component_app_gallery + - page_apps + component_app_iframe_viewer: + - page_app_detail + page_home: [] + page_login: [] + page_register: [] + page_dashboard: [] + page_recordings: [] + page_recording_detail: [] + page_apps: [] + page_app_detail: [] +type_mappings: + model_user: + id: uuid + email: string + name: string + password_hash: string + model_recording: + id: uuid + user_id: uuid + title: string + audio_file_path: string + duration: integer + transcript: text + summary: text + is_transcribing: boolean + model_generated_app: + id: uuid + user_id: uuid + recording_id: uuid + title: string + description: text + html_content: text + prd_content: text + ui_ux_design: text + app_type: string + status: enum + api_register_user: + id: uuid + email: string + name: string + created_at: datetime + api_login_user: + user: object + token: string + api_logout_user: + success: boolean + api_get_current_user: + id: uuid + email: string + name: string + created_at: datetime + api_list_recordings: + recordings: array + total: integer + api_create_recording: + id: uuid + title: string + audio_file_path: string + duration: integer + created_at: datetime + api_get_recording: + id: uuid + title: string + audio_file_path: string + duration: integer + transcript: string + summary: string + is_transcribing: boolean + created_at: datetime + api_delete_recording: + success: boolean + api_transcribe_recording: + recording_id: uuid + is_transcribing: boolean + api_summarize_recording: + recording_id: uuid + summary: string + api_list_apps: + apps: array + total: integer + api_generate_app: + id: uuid + recording_id: uuid + status: string + api_get_app: + id: uuid + recording_id: uuid + title: string + description: string + html_content: string + app_type: string + status: string + created_at: datetime + api_delete_app: + success: boolean + component_header: + user: User | null + component_sidebar: + activePath: string + component_hero: {} + component_features: {} + component_login_form: {} + component_register_form: {} + component_record_button: + isRecording: boolean + isTranscribing: boolean + component_wake_word_indicator: + isListening: boolean + wakeWord: string + component_recording_list: + recordings: Recording[] + isLoading: boolean + component_recording_card: + recording: Recording + showActions: boolean + component_audio_player: + audioUrl: string + duration: integer + component_transcript_viewer: + transcript: string + isLive: boolean + component_summary_display: + summary: string + isLoading: boolean + component_app_gallery: + apps: GeneratedApp[] + isLoading: boolean + component_app_card: + app: GeneratedApp + showActions: boolean + component_app_iframe_viewer: + htmlContent: string + title: string +issues: + circular_dependencies: [] +statistics: + total_entities: 41 + total_relations: 110 + by_type: + database: 3 + api: 14 + component: 16 + page: 8 + by_layer: + 1_database: 3 + 2_api: 14 + 3_component: 16 + 4_page: 8 diff --git a/.workflow/versions/v001/requirements/summary.yml b/.workflow/versions/v001/requirements/summary.yml new file mode 100644 index 0000000..7563037 --- /dev/null +++ b/.workflow/versions/v001/requirements/summary.yml @@ -0,0 +1,135 @@ +feature: "Voice recording app with AI summarization and automatic app generation" +gathered_at: "2024-12-19T06:00:00Z" +questions_asked: 12 +mode: auto + +requirements: + recording: + storage: permanent + metadata: true + playback: true + history: true + + summarization: + modes: + - real_time_transcription + - post_recording_summary + toggle_available: true + + app_generation: + type: ai_decides_based_on_content + display: embedded_iframe_preview + storage: persistent_with_history + triggers: + - wake_word: "エウレカアプリ作って" + mode: always_listening + + ai_services: + speech_to_text: + provider: local_whisper + implementation: node_whisper_binding + text_generation: + provider: google_gemini + model: gemini-3.0-flash-preview + + authentication: + method: email_password + features: + - registration + - login + - password_reset + + ui_design: + style: professional_clean + features: + - minimal_animations + - clean_typography + - subtle_colors + + organization: + method: chronological_list + features: + - search + - date_sorting + + dashboard: + layout: recording_controls_recent + features: + - big_record_button + - recent_recordings_list + - quick_summary + +data_model: + user_fields: + - email + - password_hash + - name + - created_at + + recording_fields: + - title + - audio_file_path + - duration + - transcript + - summary + - created_at + - user_id + + generated_app_fields: + - title + - html_content + - source_recording_id + - prd_content + - ui_ux_design + - created_at + - user_id + +ui_components: + pages: + - login + - register + - dashboard + - recording_detail + - app_gallery + - app_preview + components: + - recording_button + - recording_list + - recording_card + - audio_player + - transcript_viewer + - summary_display + - app_iframe_viewer + - wake_word_indicator + - header + - sidebar + +api_endpoints: + auth: + - POST /api/auth/register + - POST /api/auth/login + - POST /api/auth/logout + - POST /api/auth/forgot-password + recordings: + - GET /api/recordings + - POST /api/recordings + - GET /api/recordings/:id + - DELETE /api/recordings/:id + transcription: + - POST /api/transcribe + - POST /api/summarize + apps: + - GET /api/apps + - POST /api/apps/generate + - GET /api/apps/:id + - DELETE /api/apps/:id + +acceptance_criteria: + - User can register and login with email/password + - User can start recording audio with big button + - Recording shows real-time transcription (when toggled on) + - Recording generates summary after stopping + - Wake word "エウレカアプリ作って" triggers app generation + - Generated app displays in embedded iframe + - User can browse history of recordings and generated apps + - All data persists across sessions diff --git a/.workflow/versions/v001/session.yml b/.workflow/versions/v001/session.yml new file mode 100644 index 0000000..6f99dfb --- /dev/null +++ b/.workflow/versions/v001/session.yml @@ -0,0 +1,31 @@ +version: v001 +feature: Voice recording app with AI summarization and automatic app generation triggered + by wakeup-word エウレカアプリ作って +session_id: workflow_20251219_055758 +parent_version: null +status: completed +started_at: '2025-12-19T05:57:58.578500' +completed_at: '2025-12-19T06:18:09.905506' +current_phase: COMPLETING +approvals: + design: + status: approved + approved_by: user + approved_at: '2025-12-19T06:08:55.127231' + rejection_reason: null + implementation: + status: approved + approved_by: user + approved_at: '2025-12-19T06:18:03.185659' + rejection_reason: null +task_sessions: [] +summary: + total_tasks: 0 + tasks_completed: 0 + entities_created: 0 + entities_updated: 0 + entities_deleted: 0 + files_created: 0 + files_updated: 0 + files_deleted: 0 +updated_at: '2025-12-19T06:18:09.905513' diff --git a/.workflow/versions/v001/session.yml.bak b/.workflow/versions/v001/session.yml.bak new file mode 100644 index 0000000..00516e6 --- /dev/null +++ b/.workflow/versions/v001/session.yml.bak @@ -0,0 +1,31 @@ +version: v001 +feature: Voice recording app with AI summarization and automatic app generation triggered + by wakeup-word エウレカアプリ作って +session_id: workflow_20251219_055758 +parent_version: null +status: pending +started_at: '2025-12-19T05:57:58.578500' +completed_at: null +current_phase: IMPL_APPROVED +approvals: + design: + status: approved + approved_by: user + approved_at: '2025-12-19T06:08:55.127231' + rejection_reason: null + implementation: + status: approved + approved_by: user + approved_at: '2025-12-19T06:18:03.185659' + rejection_reason: null +task_sessions: [] +summary: + total_tasks: 0 + tasks_completed: 0 + entities_created: 0 + entities_updated: 0 + entities_deleted: 0 + files_created: 0 + files_updated: 0 + files_deleted: 0 +updated_at: '2025-12-19T06:18:03.187307' diff --git a/.workflow/versions/v001/snapshot_after/manifest.json b/.workflow/versions/v001/snapshot_after/manifest.json new file mode 100644 index 0000000..bc0cc72 --- /dev/null +++ b/.workflow/versions/v001/snapshot_after/manifest.json @@ -0,0 +1,321 @@ +{ + "project": { + "name": "note-to-app", + "version": "0.1.0", + "description": "Voice recording app with AI-powered summarization and automatic app generation" + }, + "state": { + "current_phase": "DESIGNING" + }, + "entities": { + "data_models": [ + { + "id": "model_user", + "name": "User", + "status": "PENDING", + "file_path": "prisma/schema.prisma", + "description": "Application user account with email/password authentication" + }, + { + "id": "model_recording", + "name": "Recording", + "status": "PENDING", + "file_path": "prisma/schema.prisma", + "description": "Voice recording with transcript and summary" + }, + { + "id": "model_generated_app", + "name": "GeneratedApp", + "status": "PENDING", + "file_path": "prisma/schema.prisma", + "description": "AI-generated application from recording content" + } + ], + "api_endpoints": [ + { + "id": "api_register_user", + "method": "POST", + "path": "/api/auth/register", + "status": "PENDING", + "file_path": "app/api/auth/register/route.ts", + "description": "Register a new user" + }, + { + "id": "api_login_user", + "method": "POST", + "path": "/api/auth/login", + "status": "PENDING", + "file_path": "app/api/auth/login/route.ts", + "description": "Login user" + }, + { + "id": "api_logout_user", + "method": "POST", + "path": "/api/auth/logout", + "status": "PENDING", + "file_path": "app/api/auth/logout/route.ts", + "description": "Logout user" + }, + { + "id": "api_get_current_user", + "method": "GET", + "path": "/api/auth/me", + "status": "PENDING", + "file_path": "app/api/auth/me/route.ts", + "description": "Get current user" + }, + { + "id": "api_list_recordings", + "method": "GET", + "path": "/api/recordings", + "status": "PENDING", + "file_path": "app/api/recordings/route.ts", + "description": "List user recordings" + }, + { + "id": "api_create_recording", + "method": "POST", + "path": "/api/recordings", + "status": "PENDING", + "file_path": "app/api/recordings/route.ts", + "description": "Create new recording" + }, + { + "id": "api_get_recording", + "method": "GET", + "path": "/api/recordings/[id]", + "status": "PENDING", + "file_path": "app/api/recordings/[id]/route.ts", + "description": "Get single recording" + }, + { + "id": "api_delete_recording", + "method": "DELETE", + "path": "/api/recordings/[id]", + "status": "PENDING", + "file_path": "app/api/recordings/[id]/route.ts", + "description": "Delete recording" + }, + { + "id": "api_transcribe_recording", + "method": "POST", + "path": "/api/recordings/[id]/transcribe", + "status": "PENDING", + "file_path": "app/api/recordings/[id]/transcribe/route.ts", + "description": "Transcribe recording" + }, + { + "id": "api_summarize_recording", + "method": "POST", + "path": "/api/recordings/[id]/summarize", + "status": "PENDING", + "file_path": "app/api/recordings/[id]/summarize/route.ts", + "description": "Summarize recording" + }, + { + "id": "api_list_apps", + "method": "GET", + "path": "/api/apps", + "status": "PENDING", + "file_path": "app/api/apps/route.ts", + "description": "List generated apps" + }, + { + "id": "api_generate_app", + "method": "POST", + "path": "/api/apps/generate", + "status": "PENDING", + "file_path": "app/api/apps/generate/route.ts", + "description": "Generate app from recording" + }, + { + "id": "api_get_app", + "method": "GET", + "path": "/api/apps/[id]", + "status": "PENDING", + "file_path": "app/api/apps/[id]/route.ts", + "description": "Get single generated app" + }, + { + "id": "api_delete_app", + "method": "DELETE", + "path": "/api/apps/[id]", + "status": "PENDING", + "file_path": "app/api/apps/[id]/route.ts", + "description": "Delete generated app" + } + ], + "components": [ + { + "id": "component_header", + "name": "Header", + "status": "PENDING", + "file_path": "app/components/Header.tsx", + "description": "Navigation header" + }, + { + "id": "component_sidebar", + "name": "Sidebar", + "status": "PENDING", + "file_path": "app/components/Sidebar.tsx", + "description": "Navigation sidebar" + }, + { + "id": "component_hero", + "name": "Hero", + "status": "PENDING", + "file_path": "app/components/Hero.tsx", + "description": "Hero section for landing page" + }, + { + "id": "component_features", + "name": "Features", + "status": "PENDING", + "file_path": "app/components/Features.tsx", + "description": "Features section for landing page" + }, + { + "id": "component_login_form", + "name": "LoginForm", + "status": "PENDING", + "file_path": "app/components/LoginForm.tsx", + "description": "Email/password login form" + }, + { + "id": "component_register_form", + "name": "RegisterForm", + "status": "PENDING", + "file_path": "app/components/RegisterForm.tsx", + "description": "Registration form" + }, + { + "id": "component_record_button", + "name": "RecordButton", + "status": "PENDING", + "file_path": "app/components/RecordButton.tsx", + "description": "Main record/stop button" + }, + { + "id": "component_wake_word_indicator", + "name": "WakeWordIndicator", + "status": "PENDING", + "file_path": "app/components/WakeWordIndicator.tsx", + "description": "Shows wake word listening status" + }, + { + "id": "component_recording_list", + "name": "RecordingList", + "status": "PENDING", + "file_path": "app/components/RecordingList.tsx", + "description": "List of recordings" + }, + { + "id": "component_recording_card", + "name": "RecordingCard", + "status": "PENDING", + "file_path": "app/components/RecordingCard.tsx", + "description": "Single recording preview" + }, + { + "id": "component_audio_player", + "name": "AudioPlayer", + "status": "PENDING", + "file_path": "app/components/AudioPlayer.tsx", + "description": "Audio playback controls" + }, + { + "id": "component_transcript_viewer", + "name": "TranscriptViewer", + "status": "PENDING", + "file_path": "app/components/TranscriptViewer.tsx", + "description": "Show live/final transcript" + }, + { + "id": "component_summary_display", + "name": "SummaryDisplay", + "status": "PENDING", + "file_path": "app/components/SummaryDisplay.tsx", + "description": "Show AI summary" + }, + { + "id": "component_app_gallery", + "name": "AppGallery", + "status": "PENDING", + "file_path": "app/components/AppGallery.tsx", + "description": "Grid of generated apps" + }, + { + "id": "component_app_card", + "name": "AppCard", + "status": "PENDING", + "file_path": "app/components/AppCard.tsx", + "description": "Single app preview card" + }, + { + "id": "component_app_iframe_viewer", + "name": "AppIframeViewer", + "status": "PENDING", + "file_path": "app/components/AppIframeViewer.tsx", + "description": "Iframe to display generated app" + } + ], + "pages": [ + { + "id": "page_home", + "name": "Home", + "status": "PENDING", + "file_path": "app/page.tsx", + "description": "Landing page for non-authenticated users" + }, + { + "id": "page_login", + "name": "Login", + "status": "PENDING", + "file_path": "app/login/page.tsx", + "description": "Login page" + }, + { + "id": "page_register", + "name": "Register", + "status": "PENDING", + "file_path": "app/register/page.tsx", + "description": "Registration page" + }, + { + "id": "page_dashboard", + "name": "Dashboard", + "status": "PENDING", + "file_path": "app/dashboard/page.tsx", + "description": "Main dashboard with recording controls" + }, + { + "id": "page_recordings", + "name": "Recordings", + "status": "PENDING", + "file_path": "app/recordings/page.tsx", + "description": "Recordings list" + }, + { + "id": "page_recording_detail", + "name": "Recording Detail", + "status": "PENDING", + "file_path": "app/recordings/[id]/page.tsx", + "description": "Recording detail with transcript/summary" + }, + { + "id": "page_apps", + "name": "Generated Apps", + "status": "PENDING", + "file_path": "app/apps/page.tsx", + "description": "Generated apps gallery" + }, + { + "id": "page_app_detail", + "name": "App Preview", + "status": "PENDING", + "file_path": "app/apps/[id]/page.tsx", + "description": "App preview in iframe" + } + ] + } +} diff --git a/.workflow/versions/v001/snapshot_before/manifest.json b/.workflow/versions/v001/snapshot_before/manifest.json new file mode 100644 index 0000000..18e9c95 --- /dev/null +++ b/.workflow/versions/v001/snapshot_before/manifest.json @@ -0,0 +1,16 @@ +{ + "project": { + "name": "note-to-app", + "version": "0.1.0", + "description": "Voice recording app with AI-powered summarization and automatic app generation" + }, + "state": { + "current_phase": "INITIALIZING" + }, + "entities": { + "components": [], + "pages": [], + "api_endpoints": [], + "data_models": [] + } +} diff --git a/.workflow/versions/v001/tasks/task_create_api_create_recording.yml b/.workflow/versions/v001/tasks/task_create_api_create_recording.yml new file mode 100644 index 0000000..1d5e4a1 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_create_recording.yml @@ -0,0 +1,19 @@ +id: task_create_api_create_recording +type: create +title: Create Create new recording +agent: backend +entity_id: api_create_recording +entity_ids: +- api_create_recording +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_create_recording.yml +created_at: '2025-12-19T06:08:12.208271' diff --git a/.workflow/versions/v001/tasks/task_create_api_delete_app.yml b/.workflow/versions/v001/tasks/task_create_api_delete_app.yml new file mode 100644 index 0000000..9ff9250 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_delete_app.yml @@ -0,0 +1,19 @@ +id: task_create_api_delete_app +type: create +title: Create Delete generated app +agent: backend +entity_id: api_delete_app +entity_ids: +- api_delete_app +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_model_generated_app +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_delete_app.yml +created_at: '2025-12-19T06:08:12.210922' diff --git a/.workflow/versions/v001/tasks/task_create_api_delete_recording.yml b/.workflow/versions/v001/tasks/task_create_api_delete_recording.yml new file mode 100644 index 0000000..bb313bf --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_delete_recording.yml @@ -0,0 +1,19 @@ +id: task_create_api_delete_recording +type: create +title: Create Delete recording +agent: backend +entity_id: api_delete_recording +entity_ids: +- api_delete_recording +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_delete_recording.yml +created_at: '2025-12-19T06:08:12.208577' diff --git a/.workflow/versions/v001/tasks/task_create_api_generate_app.yml b/.workflow/versions/v001/tasks/task_create_api_generate_app.yml new file mode 100644 index 0000000..ac9b21a --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_generate_app.yml @@ -0,0 +1,21 @@ +id: task_create_api_generate_app +type: create +title: Create Generate app from recording +agent: backend +entity_id: api_generate_app +entity_ids: +- api_generate_app +status: pending +layer: 5 +parallel_group: layer_5 +complexity: medium +dependencies: +- task_create_api_summarize_recording +- task_create_model_generated_app +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_generate_app.yml +created_at: '2025-12-19T06:08:12.214103' diff --git a/.workflow/versions/v001/tasks/task_create_api_get_app.yml b/.workflow/versions/v001/tasks/task_create_api_get_app.yml new file mode 100644 index 0000000..75a3868 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_get_app.yml @@ -0,0 +1,19 @@ +id: task_create_api_get_app +type: create +title: Create Get single generated app +agent: backend +entity_id: api_get_app +entity_ids: +- api_get_app +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_model_generated_app +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_get_app.yml +created_at: '2025-12-19T06:08:12.211265' diff --git a/.workflow/versions/v001/tasks/task_create_api_get_current_user.yml b/.workflow/versions/v001/tasks/task_create_api_get_current_user.yml new file mode 100644 index 0000000..bdf4485 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_get_current_user.yml @@ -0,0 +1,19 @@ +id: task_create_api_get_current_user +type: create +title: Create Get current user +agent: backend +entity_id: api_get_current_user +entity_ids: +- api_get_current_user +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_model_user +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_get_current_user.yml +created_at: '2025-12-19T06:08:12.206103' diff --git a/.workflow/versions/v001/tasks/task_create_api_get_recording.yml b/.workflow/versions/v001/tasks/task_create_api_get_recording.yml new file mode 100644 index 0000000..597ddd9 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_get_recording.yml @@ -0,0 +1,19 @@ +id: task_create_api_get_recording +type: create +title: Create Get single recording +agent: backend +entity_id: api_get_recording +entity_ids: +- api_get_recording +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_get_recording.yml +created_at: '2025-12-19T06:08:12.208884' diff --git a/.workflow/versions/v001/tasks/task_create_api_list_apps.yml b/.workflow/versions/v001/tasks/task_create_api_list_apps.yml new file mode 100644 index 0000000..b661aec --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_list_apps.yml @@ -0,0 +1,19 @@ +id: task_create_api_list_apps +type: create +title: Create List generated apps +agent: backend +entity_id: api_list_apps +entity_ids: +- api_list_apps +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_model_generated_app +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_list_apps.yml +created_at: '2025-12-19T06:08:12.211605' diff --git a/.workflow/versions/v001/tasks/task_create_api_list_recordings.yml b/.workflow/versions/v001/tasks/task_create_api_list_recordings.yml new file mode 100644 index 0000000..e8b2226 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_list_recordings.yml @@ -0,0 +1,19 @@ +id: task_create_api_list_recordings +type: create +title: Create List user recordings +agent: backend +entity_id: api_list_recordings +entity_ids: +- api_list_recordings +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_list_recordings.yml +created_at: '2025-12-19T06:08:12.209195' diff --git a/.workflow/versions/v001/tasks/task_create_api_login_user.yml b/.workflow/versions/v001/tasks/task_create_api_login_user.yml new file mode 100644 index 0000000..6eac995 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_login_user.yml @@ -0,0 +1,19 @@ +id: task_create_api_login_user +type: create +title: Create Login user +agent: backend +entity_id: api_login_user +entity_ids: +- api_login_user +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_model_user +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_login_user.yml +created_at: '2025-12-19T06:08:12.206414' diff --git a/.workflow/versions/v001/tasks/task_create_api_logout_user.yml b/.workflow/versions/v001/tasks/task_create_api_logout_user.yml new file mode 100644 index 0000000..089c860 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_logout_user.yml @@ -0,0 +1,18 @@ +id: task_create_api_logout_user +type: create +title: Create Logout user +agent: backend +entity_id: api_logout_user +entity_ids: +- api_logout_user +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_logout_user.yml +created_at: '2025-12-19T06:08:12.201280' diff --git a/.workflow/versions/v001/tasks/task_create_api_register_user.yml b/.workflow/versions/v001/tasks/task_create_api_register_user.yml new file mode 100644 index 0000000..dae0594 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_register_user.yml @@ -0,0 +1,19 @@ +id: task_create_api_register_user +type: create +title: Create Register a new user +agent: backend +entity_id: api_register_user +entity_ids: +- api_register_user +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_model_user +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_register_user.yml +created_at: '2025-12-19T06:08:12.206716' diff --git a/.workflow/versions/v001/tasks/task_create_api_summarize_recording.yml b/.workflow/versions/v001/tasks/task_create_api_summarize_recording.yml new file mode 100644 index 0000000..59e7d3c --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_summarize_recording.yml @@ -0,0 +1,20 @@ +id: task_create_api_summarize_recording +type: create +title: Create Summarize recording +agent: backend +entity_id: api_summarize_recording +entity_ids: +- api_summarize_recording +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_api_transcribe_recording +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_summarize_recording.yml +created_at: '2025-12-19T06:08:12.211943' diff --git a/.workflow/versions/v001/tasks/task_create_api_transcribe_recording.yml b/.workflow/versions/v001/tasks/task_create_api_transcribe_recording.yml new file mode 100644 index 0000000..b23fe63 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_api_transcribe_recording.yml @@ -0,0 +1,19 @@ +id: task_create_api_transcribe_recording +type: create +title: Create Transcribe recording +agent: backend +entity_id: api_transcribe_recording +entity_ids: +- api_transcribe_recording +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/api_transcribe_recording.yml +created_at: '2025-12-19T06:08:12.209526' diff --git a/.workflow/versions/v001/tasks/task_create_component_app_card.yml b/.workflow/versions/v001/tasks/task_create_component_app_card.yml new file mode 100644 index 0000000..2ea2947 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_app_card.yml @@ -0,0 +1,18 @@ +id: task_create_component_app_card +type: create +title: Create AppCard +agent: frontend +entity_id: component_app_card +entity_ids: +- component_app_card +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_app_card.yml +created_at: '2025-12-19T06:08:12.201586' diff --git a/.workflow/versions/v001/tasks/task_create_component_app_gallery.yml b/.workflow/versions/v001/tasks/task_create_component_app_gallery.yml new file mode 100644 index 0000000..23eba12 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_app_gallery.yml @@ -0,0 +1,19 @@ +id: task_create_component_app_gallery +type: create +title: Create AppGallery +agent: frontend +entity_id: component_app_gallery +entity_ids: +- component_app_gallery +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_component_app_card +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_app_gallery.yml +created_at: '2025-12-19T06:08:12.207023' diff --git a/.workflow/versions/v001/tasks/task_create_component_app_iframe_viewer.yml b/.workflow/versions/v001/tasks/task_create_component_app_iframe_viewer.yml new file mode 100644 index 0000000..bd430e2 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_app_iframe_viewer.yml @@ -0,0 +1,18 @@ +id: task_create_component_app_iframe_viewer +type: create +title: Create AppIframeViewer +agent: frontend +entity_id: component_app_iframe_viewer +entity_ids: +- component_app_iframe_viewer +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_app_iframe_viewer.yml +created_at: '2025-12-19T06:08:12.201891' diff --git a/.workflow/versions/v001/tasks/task_create_component_audio_player.yml b/.workflow/versions/v001/tasks/task_create_component_audio_player.yml new file mode 100644 index 0000000..01e9644 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_audio_player.yml @@ -0,0 +1,18 @@ +id: task_create_component_audio_player +type: create +title: Create AudioPlayer +agent: frontend +entity_id: component_audio_player +entity_ids: +- component_audio_player +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_audio_player.yml +created_at: '2025-12-19T06:08:12.202200' diff --git a/.workflow/versions/v001/tasks/task_create_component_features.yml b/.workflow/versions/v001/tasks/task_create_component_features.yml new file mode 100644 index 0000000..e951bc2 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_features.yml @@ -0,0 +1,18 @@ +id: task_create_component_features +type: create +title: Create Features +agent: frontend +entity_id: component_features +entity_ids: +- component_features +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_features.yml +created_at: '2025-12-19T06:08:12.202720' diff --git a/.workflow/versions/v001/tasks/task_create_component_header.yml b/.workflow/versions/v001/tasks/task_create_component_header.yml new file mode 100644 index 0000000..34adf63 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_header.yml @@ -0,0 +1,18 @@ +id: task_create_component_header +type: create +title: Create Header +agent: frontend +entity_id: component_header +entity_ids: +- component_header +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_header.yml +created_at: '2025-12-19T06:08:12.203099' diff --git a/.workflow/versions/v001/tasks/task_create_component_hero.yml b/.workflow/versions/v001/tasks/task_create_component_hero.yml new file mode 100644 index 0000000..4a28fb6 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_hero.yml @@ -0,0 +1,18 @@ +id: task_create_component_hero +type: create +title: Create Hero +agent: frontend +entity_id: component_hero +entity_ids: +- component_hero +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_hero.yml +created_at: '2025-12-19T06:08:12.203427' diff --git a/.workflow/versions/v001/tasks/task_create_component_login_form.yml b/.workflow/versions/v001/tasks/task_create_component_login_form.yml new file mode 100644 index 0000000..6483ce1 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_login_form.yml @@ -0,0 +1,19 @@ +id: task_create_component_login_form +type: create +title: Create LoginForm +agent: frontend +entity_id: component_login_form +entity_ids: +- component_login_form +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_api_login_user +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_login_form.yml +created_at: '2025-12-19T06:08:12.209879' diff --git a/.workflow/versions/v001/tasks/task_create_component_record_button.yml b/.workflow/versions/v001/tasks/task_create_component_record_button.yml new file mode 100644 index 0000000..52e67b1 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_record_button.yml @@ -0,0 +1,18 @@ +id: task_create_component_record_button +type: create +title: Create RecordButton +agent: frontend +entity_id: component_record_button +entity_ids: +- component_record_button +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_record_button.yml +created_at: '2025-12-19T06:08:12.203732' diff --git a/.workflow/versions/v001/tasks/task_create_component_recording_card.yml b/.workflow/versions/v001/tasks/task_create_component_recording_card.yml new file mode 100644 index 0000000..89b57fc --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_recording_card.yml @@ -0,0 +1,18 @@ +id: task_create_component_recording_card +type: create +title: Create RecordingCard +agent: frontend +entity_id: component_recording_card +entity_ids: +- component_recording_card +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_recording_card.yml +created_at: '2025-12-19T06:08:12.204045' diff --git a/.workflow/versions/v001/tasks/task_create_component_recording_list.yml b/.workflow/versions/v001/tasks/task_create_component_recording_list.yml new file mode 100644 index 0000000..3407501 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_recording_list.yml @@ -0,0 +1,19 @@ +id: task_create_component_recording_list +type: create +title: Create RecordingList +agent: frontend +entity_id: component_recording_list +entity_ids: +- component_recording_list +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_component_recording_card +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_recording_list.yml +created_at: '2025-12-19T06:08:12.207335' diff --git a/.workflow/versions/v001/tasks/task_create_component_register_form.yml b/.workflow/versions/v001/tasks/task_create_component_register_form.yml new file mode 100644 index 0000000..1bac783 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_register_form.yml @@ -0,0 +1,19 @@ +id: task_create_component_register_form +type: create +title: Create RegisterForm +agent: frontend +entity_id: component_register_form +entity_ids: +- component_register_form +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_api_register_user +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_register_form.yml +created_at: '2025-12-19T06:08:12.210220' diff --git a/.workflow/versions/v001/tasks/task_create_component_sidebar.yml b/.workflow/versions/v001/tasks/task_create_component_sidebar.yml new file mode 100644 index 0000000..3803096 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_sidebar.yml @@ -0,0 +1,18 @@ +id: task_create_component_sidebar +type: create +title: Create Sidebar +agent: frontend +entity_id: component_sidebar +entity_ids: +- component_sidebar +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_sidebar.yml +created_at: '2025-12-19T06:08:12.204368' diff --git a/.workflow/versions/v001/tasks/task_create_component_summary_display.yml b/.workflow/versions/v001/tasks/task_create_component_summary_display.yml new file mode 100644 index 0000000..df04567 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_summary_display.yml @@ -0,0 +1,18 @@ +id: task_create_component_summary_display +type: create +title: Create SummaryDisplay +agent: frontend +entity_id: component_summary_display +entity_ids: +- component_summary_display +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_summary_display.yml +created_at: '2025-12-19T06:08:12.204668' diff --git a/.workflow/versions/v001/tasks/task_create_component_transcript_viewer.yml b/.workflow/versions/v001/tasks/task_create_component_transcript_viewer.yml new file mode 100644 index 0000000..3a115b8 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_transcript_viewer.yml @@ -0,0 +1,18 @@ +id: task_create_component_transcript_viewer +type: create +title: Create TranscriptViewer +agent: frontend +entity_id: component_transcript_viewer +entity_ids: +- component_transcript_viewer +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_transcript_viewer.yml +created_at: '2025-12-19T06:08:12.204979' diff --git a/.workflow/versions/v001/tasks/task_create_component_wake_word_indicator.yml b/.workflow/versions/v001/tasks/task_create_component_wake_word_indicator.yml new file mode 100644 index 0000000..846ab15 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_component_wake_word_indicator.yml @@ -0,0 +1,18 @@ +id: task_create_component_wake_word_indicator +type: create +title: Create WakeWordIndicator +agent: frontend +entity_id: component_wake_word_indicator +entity_ids: +- component_wake_word_indicator +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/component_wake_word_indicator.yml +created_at: '2025-12-19T06:08:12.205295' diff --git a/.workflow/versions/v001/tasks/task_create_model_generated_app.yml b/.workflow/versions/v001/tasks/task_create_model_generated_app.yml new file mode 100644 index 0000000..10b9e90 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_model_generated_app.yml @@ -0,0 +1,20 @@ +id: task_create_model_generated_app +type: create +title: Create GeneratedApp +agent: backend +entity_id: model_generated_app +entity_ids: +- model_generated_app +status: pending +layer: 3 +parallel_group: layer_3 +complexity: medium +dependencies: +- task_create_model_user +- task_create_model_recording +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/model_generated_app.yml +created_at: '2025-12-19T06:08:12.210565' diff --git a/.workflow/versions/v001/tasks/task_create_model_recording.yml b/.workflow/versions/v001/tasks/task_create_model_recording.yml new file mode 100644 index 0000000..0bd2dfb --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_model_recording.yml @@ -0,0 +1,19 @@ +id: task_create_model_recording +type: create +title: Create Recording +agent: backend +entity_id: model_recording +entity_ids: +- model_recording +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_model_user +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/model_recording.yml +created_at: '2025-12-19T06:08:12.207652' diff --git a/.workflow/versions/v001/tasks/task_create_model_user.yml b/.workflow/versions/v001/tasks/task_create_model_user.yml new file mode 100644 index 0000000..dab4780 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_model_user.yml @@ -0,0 +1,18 @@ +id: task_create_model_user +type: create +title: Create User +agent: backend +entity_id: model_user +entity_ids: +- model_user +status: pending +layer: 1 +parallel_group: layer_1 +complexity: medium +dependencies: [] +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/model_user.yml +created_at: '2025-12-19T06:08:12.205806' diff --git a/.workflow/versions/v001/tasks/task_create_page_app_detail.yml b/.workflow/versions/v001/tasks/task_create_page_app_detail.yml new file mode 100644 index 0000000..3b0c949 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_app_detail.yml @@ -0,0 +1,22 @@ +id: task_create_page_app_detail +type: create +title: Create App Preview +agent: frontend +entity_id: page_app_detail +entity_ids: +- page_app_detail +status: pending +layer: 5 +parallel_group: layer_5 +complexity: medium +dependencies: +- task_create_component_header +- task_create_component_app_iframe_viewer +- task_create_api_get_app +- task_create_component_sidebar +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_app_detail.yml +created_at: '2025-12-19T06:08:12.214450' diff --git a/.workflow/versions/v001/tasks/task_create_page_apps.yml b/.workflow/versions/v001/tasks/task_create_page_apps.yml new file mode 100644 index 0000000..8b9dda1 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_apps.yml @@ -0,0 +1,23 @@ +id: task_create_page_apps +type: create +title: Create Generated Apps +agent: frontend +entity_id: page_apps +entity_ids: +- page_apps +status: pending +layer: 5 +parallel_group: layer_5 +complexity: medium +dependencies: +- task_create_api_list_apps +- task_create_component_app_card +- task_create_component_app_gallery +- task_create_component_header +- task_create_component_sidebar +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_apps.yml +created_at: '2025-12-19T06:08:12.214799' diff --git a/.workflow/versions/v001/tasks/task_create_page_dashboard.yml b/.workflow/versions/v001/tasks/task_create_page_dashboard.yml new file mode 100644 index 0000000..49489bb --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_dashboard.yml @@ -0,0 +1,25 @@ +id: task_create_page_dashboard +type: create +title: Create Dashboard +agent: frontend +entity_id: page_dashboard +entity_ids: +- page_dashboard +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_component_recording_list +- task_create_api_list_recordings +- task_create_component_record_button +- task_create_api_get_current_user +- task_create_component_wake_word_indicator +- task_create_component_header +- task_create_component_sidebar +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_dashboard.yml +created_at: '2025-12-19T06:08:12.212305' diff --git a/.workflow/versions/v001/tasks/task_create_page_home.yml b/.workflow/versions/v001/tasks/task_create_page_home.yml new file mode 100644 index 0000000..f993cf3 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_home.yml @@ -0,0 +1,21 @@ +id: task_create_page_home +type: create +title: Create Home +agent: frontend +entity_id: page_home +entity_ids: +- page_home +status: pending +layer: 2 +parallel_group: layer_2 +complexity: medium +dependencies: +- task_create_component_features +- task_create_component_header +- task_create_component_hero +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_home.yml +created_at: '2025-12-19T06:08:12.207954' diff --git a/.workflow/versions/v001/tasks/task_create_page_login.yml b/.workflow/versions/v001/tasks/task_create_page_login.yml new file mode 100644 index 0000000..1bfe40d --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_login.yml @@ -0,0 +1,19 @@ +id: task_create_page_login +type: create +title: Create Login +agent: frontend +entity_id: page_login +entity_ids: +- page_login +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_component_login_form +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_login.yml +created_at: '2025-12-19T06:08:12.212724' diff --git a/.workflow/versions/v001/tasks/task_create_page_recording_detail.yml b/.workflow/versions/v001/tasks/task_create_page_recording_detail.yml new file mode 100644 index 0000000..4f7bc8c --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_recording_detail.yml @@ -0,0 +1,24 @@ +id: task_create_page_recording_detail +type: create +title: Create Recording Detail +agent: frontend +entity_id: page_recording_detail +entity_ids: +- page_recording_detail +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_component_transcript_viewer +- task_create_component_summary_display +- task_create_api_get_recording +- task_create_component_audio_player +- task_create_component_header +- task_create_component_sidebar +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_recording_detail.yml +created_at: '2025-12-19T06:08:12.213053' diff --git a/.workflow/versions/v001/tasks/task_create_page_recordings.yml b/.workflow/versions/v001/tasks/task_create_page_recordings.yml new file mode 100644 index 0000000..d510e53 --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_recordings.yml @@ -0,0 +1,23 @@ +id: task_create_page_recordings +type: create +title: Create Recordings +agent: frontend +entity_id: page_recordings +entity_ids: +- page_recordings +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_api_list_recordings +- task_create_component_header +- task_create_component_recording_list +- task_create_component_recording_card +- task_create_component_sidebar +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_recordings.yml +created_at: '2025-12-19T06:08:12.213431' diff --git a/.workflow/versions/v001/tasks/task_create_page_register.yml b/.workflow/versions/v001/tasks/task_create_page_register.yml new file mode 100644 index 0000000..9be047a --- /dev/null +++ b/.workflow/versions/v001/tasks/task_create_page_register.yml @@ -0,0 +1,19 @@ +id: task_create_page_register +type: create +title: Create Register +agent: frontend +entity_id: page_register +entity_ids: +- page_register +status: pending +layer: 4 +parallel_group: layer_4 +complexity: medium +dependencies: +- task_create_component_register_form +external_dependencies: [] +context: + design_version: 1 + workflow_version: v001 + context_snapshot_path: .workflow/versions/v001/contexts/page_register.yml +created_at: '2025-12-19T06:08:12.213784' diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8c5d9de --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,1252 @@ +# Project Instructions + +## Overview + +This project uses the **Guardrail Workflow System** - a hook-enforced development workflow that **prevents AI from bypassing the design-approve-implement cycle**. + +--- + +## Eureka Platform Environment + +This project deploys to **Eureka Platform** with the following services available: + +### Injected Environment Variables + +These variables are **automatically injected** at runtime when deployed: + +```bash +# Database (PostgreSQL) +DATABASE_URL="postgresql://appuser:supersecret@postgres:5432/eurekalabo" + +# Object Storage (MinIO - S3 compatible) +MINIO_ENDPOINT="minio" +MINIO_PORT="9000" +MINIO_ROOT_USER="minioadmin" +MINIO_ROOT_PASSWORD="minioadmin" +MINIO_BUCKET_NAME="eurekalabo-files" +MINIO_USE_SSL="false" +MINIO_PUBLIC_URL="https://minio.162-43-92-100.nip.io" +``` + +### MUST Use These Variables + +When generating code that uses database or file storage: + +**Database (Prisma/Drizzle/etc):** +```typescript +// Use DATABASE_URL - automatically available +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +**File Storage (MinIO/S3):** +```typescript +import { Client } from 'minio'; + +const minio = new Client({ + endPoint: process.env.MINIO_ENDPOINT!, + port: parseInt(process.env.MINIO_PORT || '9000'), + useSSL: process.env.MINIO_USE_SSL === 'true', + accessKey: process.env.MINIO_ROOT_USER!, + secretKey: process.env.MINIO_ROOT_PASSWORD!, +}); + +// Bucket name +const bucket = process.env.MINIO_BUCKET_NAME!; +``` + +### DO NOT Hardcode + +Never hardcode database URLs or storage credentials. Always use `process.env.*` variables. + +**See:** `.claude/references/eureka-infrastructure.md` for full documentation. + +--- + +## Workflow Enforcement Architecture + +### How AI is Forced to Follow Workflows + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ HOOK ENFORCEMENT LAYER │ +├─────────────────────────────────────────────────────────────────┤ +│ PreToolUse Hooks intercept ALL file operations: │ +│ │ +│ Write/Edit/MultiEdit → validate_workflow.py → validate_write.py│ +│ Task (agents) → validate_workflow.py (agent restrictions)│ +│ MCP tools → validate_workflow.py (serena, morphllm) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ PHASE STATE MACHINE │ +├─────────────────────────────────────────────────────────────────┤ +│ INITIALIZING → DESIGNING → AWAITING_DESIGN_APPROVAL │ +│ │ │ │ │ +│ │ │ ┌────────┴────────┐ │ +│ │ │ ▼ ▼ │ +│ │ │ DESIGN_APPROVED DESIGN_REJECTED │ +│ │ │ │ │ │ +│ │ │ ▼ │ │ +│ │ │ IMPLEMENTING ←─────────┘ │ +│ │ │ │ │ +│ │ │ ▼ │ +│ │ │ 🔗 INTEGRATING ←──────────┐ │ +│ │ │ │ │ │ +│ │ │ ▼ │ │ +│ │ │ REVIEWING │ │ +│ │ │ │ │ │ +│ │ │ ▼ │ │ +│ │ │ 🔒 SECURITY_REVIEW ←───────┤ │ +│ │ │ │ │ │ +│ │ │ ▼ │ │ +│ │ │ AWAITING_IMPL_APPROVAL │ │ +│ │ │ │ │ │ +│ │ │ ┌─────┴─────┐ │ │ +│ │ │ ▼ ▼ │ │ +│ │ │ IMPL_APPROVED IMPL_REJECTED─┘ │ +│ │ │ │ │ +│ │ │ ▼ │ +│ │ │ COMPLETING │ +│ │ │ │ │ +│ │ │ ▼ │ +│ │ │ COMPLETED │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Tool Restrictions by Phase + +| Phase | Write/Edit | Task (Agents) | Allowed Files | +|-------|------------|---------------|---------------| +| **NO_WORKFLOW** | ✅ Allowed | ✅ Allowed | Any | +| **DESIGNING** | ⛔ Blocked | system-architect only | manifest, .workflow/, tasks/*.yml | +| **AWAITING_DESIGN_APPROVAL** | ⛔ Blocked | ⛔ Blocked | None | +| **IMPLEMENTING** | ✅ Allowed | frontend/backend only | Files in task file_paths | +| **INTEGRATING** | ✅ Allowed | integrator only | Navigation, layouts, existing pages | +| **REVIEWING** | ⛔ Blocked | quality-engineer only | None (read-only) | +| **SECURITY_REVIEW** | ⛔ Blocked | security-engineer only | None (read-only) | +| **AWAITING_IMPL_APPROVAL** | ⛔ Blocked | ⛔ Blocked | None | +| **COMPLETED** | ⛔ Blocked | ⛔ Blocked | None | + +### Enforcement Scripts + +| Script | Purpose | Exit Codes | +|--------|---------|------------| +| `validate_workflow.py` | Phase-based blocking | 0=allow, 1=block | +| `validate_write.py` | Manifest path validation | 0=allow, 1=block | +| `validate_integration.py` | Integration validation | 0=pass, 1=warn, 2=fail | +| `build_relations.py` | Build entity relationship graph | 0=success | +| `validate_relations.py` | Validate DB↔API↔Component↔Page chain | 0=pass, 1=warn, 2=fail | +| `workflow_manager.py` | State transitions | Manages .workflow/versions/ | +| `version_manager.py` | Version lifecycle | Creates/completes versions | + +### What Happens When AI Tries to Bypass + +``` +AI attempts: Write tool to create app/components/Button.tsx + +Hook intercepts → validate_workflow.py --operation write --file "app/components/Button.tsx" + +Script checks: + 1. Current phase = DESIGNING + 2. File is NOT in always_allowed list + 3. Returns exit code 1 + +Output to AI: + ⛔ WORKFLOW VIOLATION: Cannot write implementation files during DESIGNING + + Current Phase: DESIGNING + File: app/components/Button.tsx + + During DESIGNING phase, only these files can be modified: + - project_manifest.json + - .workflow/versions/vXXX/tasks/*.yml + + Complete design and get approval before implementing. +``` + +--- + +## Workflow Commands + +### Starting a Workflow + +```bash +# Automated workflow (auto-approves if validation passes) +/workflow:spawn --auto + +# Interactive workflow (stops at approval gates) +/workflow:spawn +``` + +### Checking Status + +```bash +/workflow:status # Current workflow state +/workflow:history # All workflow versions +``` + +### Approval Gates + +```bash +/workflow:approve # Approve current gate (design or implementation) +/workflow:reject # Reject with feedback +``` + +### Resuming + +```bash +/workflow:resume # Continue interrupted workflow +``` + +--- + +## Guardrail Commands (Manual Flow) + +For granular control without full orchestration: + +### Design Phase +```bash +/guardrail:init # Initialize manifest +/guardrail:design # Add entities to manifest +/guardrail:review # Request design review +/guardrail:approve # Approve design +``` + +### Implementation Phase +```bash +/guardrail:implement # Implement approved entity +/guardrail:verify # Verify implementation +/guardrail:validate # Validate manifest integrity +``` + +### Analysis +```bash +/guardrail:analyze # Analyze existing codebase +/guardrail:status # Show project status +``` + +--- + +## Project Structure + +``` +project/ +├── .claude/ +│ ├── commands/ +│ │ ├── eureka/ # Eureka utility commands +│ │ │ └── index.md # Documentation generator +│ │ ├── guardrail/ # Manual workflow commands +│ │ └── workflow/ # Orchestrated workflow commands +│ └── settings.json # Hook configurations (enforcement) +├── .workflow/ +│ ├── current.yml # Active version pointer +│ ├── index.yml # Version registry +│ └── versions/ +│ └── vXXX/ +│ ├── session.yml # Workflow state +│ └── tasks/ # Task definitions +│ └── task_*.yml +├── skills/ +│ ├── guardrail-orchestrator/ +│ │ ├── agents/ # Agent role definitions +│ │ ├── schemas/ # YAML schemas +│ │ └── scripts/ # Enforcement scripts +│ └── documentation-generator/ +│ ├── agents/ # doc-writer agent +│ ├── schemas/ # Documentation schemas +│ ├── scripts/ # Analysis & generation scripts +│ └── templates/ # HTML template +├── project_manifest.json # Entity definitions +└── CLAUDE.md # This file +``` + +--- + +## Development Guidelines + +### MANDATORY: Follow the Workflow + +1. **Never write implementation files directly** - hooks will block you +2. **Start with `/workflow:spawn`** or `/guardrail:init` +3. **Design first** - add entities to manifest +4. **Get approval** - wait for gate approval +5. **Then implement** - only after approval + +### Workflow State Files + +- `.workflow/current.yml` - Points to active version +- `.workflow/versions/vXXX/session.yml` - Phase state, approvals +- `.workflow/versions/vXXX/tasks/*.yml` - Task definitions + +### Manifest Structure + +```json +{ + "project": { "name": "...", "version": "..." }, + "state": { "current_phase": "DESIGN_PHASE" }, + "entities": { + "components": [ + { + "id": "component_button", + "name": "Button", + "status": "PENDING|APPROVED|IMPLEMENTED", + "file_path": "app/components/Button.tsx" + } + ], + "pages": [...], + "api_endpoints": [...] + } +} +``` + +--- + +## Quick Start + +### Option 1: Automated Workflow +```bash +# Single command - runs entire workflow +/workflow:spawn --auto add user authentication +``` + +### Option 2: Interactive Workflow +```bash +# Step-by-step with approval gates +/workflow:spawn add user profile page + +# Wait for design... +/workflow:approve # Approve design + +# Wait for implementation... +/workflow:approve # Approve implementation +``` + +### Option 3: Manual Guardrail +```bash +/guardrail:init my-feature +/guardrail:design +/guardrail:approve +/guardrail:implement +/guardrail:verify +``` + +--- + +## Troubleshooting + +### "WORKFLOW VIOLATION" Error + +You tried to write files outside the allowed phase. Check: +```bash +/workflow:status +``` + +Then follow the workflow: +1. If DESIGNING → finish design, get approval +2. If REVIEWING → wait for review to complete +3. If AWAITING_*_APPROVAL → approve or reject + +### "No active workflow" Error + +Start a workflow first: +```bash +/workflow:spawn +# or +/guardrail:init +``` + +### Resuming After Interruption + +```bash +/workflow:resume +``` + +--- + +## Hook Configuration Reference + +The enforcement is configured in `.claude/settings.json`: + +### Enforced Tools (ALL BLOCKED without workflow) + +| Tool | Validator | What's Checked | +|------|-----------|----------------| +| **Bash** | `validate_bash.py` | Blocks `>`, `>>`, `tee`, `sed -i`, `cp`, `mv`, `rm` | +| **Write** | `validate_workflow.py` + `validate_write.py` | Phase + manifest paths | +| **Edit** | `validate_workflow.py` + `validate_write.py` | Phase + manifest paths | +| **MultiEdit** | `validate_workflow.py` + `validate_write.py` | Phase + manifest paths | +| **NotebookEdit** | `validate_workflow.py` + `validate_write.py` | Phase + manifest paths | +| **Task** | `validate_workflow.py` | Agent type vs phase | +| **mcp__serena__*** | `validate_workflow.py` + `validate_write.py` | All Serena writes | +| **mcp__morphllm__*** | `validate_workflow.py` + `validate_write.py` | All Morphllm writes | +| **mcp__filesystem__*** | `validate_workflow.py` + `validate_write.py` | All filesystem writes | + +### Bash Commands - Blocked Patterns + +These bash patterns are **blocked** unless writing to allowed paths: + +```bash +# Redirections +echo "x" > file.txt # BLOCKED +cat > file.txt # BLOCKED +command >> file.txt # BLOCKED + +# File operations +tee file.txt # BLOCKED +cp source dest # BLOCKED +mv source dest # BLOCKED +rm file.txt # BLOCKED +touch file.txt # BLOCKED + +# In-place edits +sed -i 's/x/y/' file # BLOCKED +awk -i inplace # BLOCKED + +# Here documents +cat << EOF > file # BLOCKED +``` + +### Bash Commands - Always Allowed + +```bash +# Reading +cat file.txt # OK (no redirect) +head/tail/grep/find # OK +ls, pwd, cd # OK + +# Running +npm run build # OK +npm test # OK +node script.js # OK +python script.py # OK (no redirect) + +# Git (read operations) +git status/log/diff # OK +git branch/show # OK +``` + +### Enforcement Scripts + +| Script | Purpose | +|--------|---------| +| `validate_workflow.py` | Phase-based blocking, instructs `/workflow:spawn` | +| `validate_write.py` | Manifest path validation | +| `validate_bash.py` | Shell command pattern blocking | +| `validate_api_contract.py` | Frontend-backend API contract validation | +| `workflow_manager.py` | State machine transitions, `validate --checklist` | +| `generate_types.py` | Generate TypeScript types from design document | +| `validate_design.py` | Validate design document and generate dependency graph | +| `post_write.py` | Auto-update entity status after writes | +| `visualize_design.py` | ASCII art design visualization | + +--- + +## Review Command (Quality Gates) + +The `/workflow:review` command runs comprehensive quality checks before approving implementation. + +### Validation Checks + +| Check | Command | Blocks Approval | Mode | +|-------|---------|-----------------|------| +| Build | `npm run build` | YES | Always | +| TypeScript | `npx tsc --noEmit` | YES | Always | +| Lint | `npm run lint` | YES | --strict | +| Tests | `npm test` | YES | --strict | +| API Contract | `validate_api_contract.py` | YES | Always | +| Security | Pattern scan | WARNING | Always | + +### API Contract Validation + +The API contract validator ensures frontend and backend are aligned: + +**Frontend Detection**: +- `fetch('/api/...')` calls +- `axios.get/post/put/delete()` requests +- `useSWR()` data fetching +- Custom API clients + +**Backend Detection**: +- Next.js App Router: `app/api/*/route.ts` exports (GET, POST, etc.) +- Next.js Pages Router: `pages/api/*.ts` req.method checks +- Express-style: `router.get/post()` patterns + +**Validation Rules**: +1. **Endpoint Existence**: Frontend calls must have matching backend routes +2. **Method Match**: GET calls → GET endpoints, POST → POST, etc. +3. **Body Alignment**: POST/PUT should send bodies, GET should not +4. **Unused Detection**: Warns about backend routes not called by frontend + +### Security Scan + +Checks for common security issues: +- Hardcoded passwords, API keys, secrets +- SQL injection patterns (`query.*${`) +- XSS risks (`dangerouslySetInnerHTML`) +- Console statements in production code + +### Usage + +```bash +# Standard review (build + types + API contract + security) +/workflow:review --auto + +# Strict review (adds lint + tests) +/workflow:review --auto --strict + +# Full review (all checks) +/workflow:review --auto --full +``` + +--- + +## Security Audit Command + +For comprehensive security analysis, use the dedicated security command: + +```bash +# Quick scan (automated scanner only) +/workflow:security --quick + +# Standard scan (scanner + dependencies) +/workflow:security + +# Full audit (scanner + deps + config review + deep analysis) +/workflow:security --full +``` + +### Security Categories Checked + +| Category | CWE | OWASP | Severity | +|----------|-----|-------|----------| +| Hardcoded Secrets | CWE-798 | A07 | CRITICAL | +| SQL Injection | CWE-89 | A03 | CRITICAL | +| Command Injection | CWE-78 | A03 | CRITICAL | +| XSS | CWE-79 | A03 | HIGH | +| Path Traversal | CWE-22 | A01 | HIGH | +| NoSQL Injection | CWE-943 | A03 | HIGH | +| SSRF | CWE-918 | A10 | HIGH | +| Prototype Pollution | CWE-1321 | A03 | HIGH | +| Insecure Auth | CWE-287 | A07 | HIGH | +| CORS Misconfiguration | CWE-942 | A01 | MEDIUM | +| Sensitive Data Exposure | CWE-200 | A02 | MEDIUM | +| Insecure Dependencies | CWE-1104 | A06 | MEDIUM | + +### Exit Codes +- **0**: PASS - No critical/high issues +- **1**: WARNING - High issues found +- **2**: CRITICAL - Critical issues found (blocks deployment) + +--- + +## Documentation Generation + +Generate comprehensive project documentation for **both engineers and non-engineers** using the `/eureka:index` command. + +### Quick Start + +```bash +# Generate documentation in docs/ folder +/eureka:index + +# Generate in custom folder +/eureka:index my-docs +``` + +### Output Files + +| File | Audience | Description | +|------|----------|-------------| +| `index.html` | **Non-engineers** | Beautiful HTML - just open in browser! | +| `PROJECT_DOCUMENTATION.md` | Both | Main documentation | +| `QUICK_REFERENCE.md` | Both | One-page reference card | +| `API_REFERENCE.md` | Engineers | Detailed API documentation | +| `COMPONENTS.md` | Engineers | Component catalog | +| `analysis.yml` | Engineers | Raw project analysis data | + +### Documentation Structure + +``` +╔══════════════════════════════════════════════════════════════╗ +║ DUAL-AUDIENCE DESIGN ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ FOR NON-ENGINEERS (HTML): FOR ENGINEERS (MD): ║ +║ ───────────────────────── ───────────────────── ║ +║ • Clean, professional view • Git-friendly format ║ +║ • Technical details hidden • Editable source ║ +║ • Click to expand if needed • Full technical specs ║ +║ • Works on mobile • Code examples ║ +║ • Print to PDF • Type definitions ║ +║ ║ +╚══════════════════════════════════════════════════════════════╝ +``` + +### Documentation Sections + +| Section | Audience | Content | +|---------|----------|---------| +| Executive Summary | Everyone | What it does, who it's for | +| Architecture Overview | Everyone | Visual diagrams, tech stack | +| Getting Started | Semi-technical | Setup, installation | +| Feature Guide | Non-engineers | Plain-language descriptions | +| API Reference | Engineers | Endpoints, schemas (collapsible) | +| Component Catalog | Engineers | Props, events (collapsible) | +| Data Models | Both | ER diagrams + descriptions | +| Glossary | Non-engineers | Technical terms explained | + +### HTML Features + +The generated `index.html` includes: + +- **Responsive design** - Works on desktop, tablet, and mobile +- **Dark mode** - Automatically adapts to system preferences +- **Sidebar navigation** - Easy section jumping +- **Collapsible technical details** - Non-engineers see clean view +- **Print-friendly** - Can be saved as PDF +- **Single file** - No dependencies, just open in browser + +### For Non-Engineers + +Simply open `docs/index.html` in your web browser. You'll see: + +1. **Executive Summary** - What the project does in plain English +2. **Feature Guide** - What each feature does and how to use it +3. **Glossary** - Explanations of technical terms + +Technical details are hidden by default. Click "🔧 Technical Details" to expand if curious. + +### For Engineers + +Use the Markdown files for: + +- Version control (git-friendly) +- Easy editing and updates +- Integration with documentation systems +- Full technical specifications + +Or use the HTML and click "🔧 Technical Details" to see code examples, props, and schemas. + +### Example Output + +``` +docs/ +├── index.html # 🌐 Open this in browser! +├── PROJECT_DOCUMENTATION.md # 📄 Main docs (markdown) +├── QUICK_REFERENCE.md # 📄 Quick reference card +├── API_REFERENCE.md # 📄 API details +├── COMPONENTS.md # 📄 Component catalog +└── analysis.yml # 📊 Raw analysis data +``` + +--- + +## Template Development Guide + +This section explains how to create new **commands**, **skills**, and **agents** for the Eureka framework. + +### Templates Directory Structure + +``` +templates/ +├── .claude/ +│ ├── commands/ # Slash commands +│ │ ├── eureka/ # /eureka:* commands +│ │ │ └── index.md +│ │ ├── guardrail/ # /guardrail:* commands +│ │ │ ├── init.md +│ │ │ ├── design.md +│ │ │ └── ... +│ │ └── workflow/ # /workflow:* commands +│ │ ├── spawn.md +│ │ ├── design.md +│ │ └── ... +│ └── settings.json # Hooks configuration +│ +├── skills/ # Skill packages +│ ├── guardrail-orchestrator/ +│ │ ├── skill.yml # Skill manifest +│ │ ├── agents/ # Agent definitions +│ │ │ ├── orchestrator.yml +│ │ │ ├── architect.yml +│ │ │ └── ... +│ │ ├── schemas/ # YAML schemas +│ │ │ ├── design_document.yml +│ │ │ └── ... +│ │ └── scripts/ # Python scripts +│ │ ├── validate_workflow.py +│ │ └── ... +│ │ +│ └── documentation-generator/ +│ ├── skill.yml +│ ├── agents/ +│ │ └── doc-writer.yml +│ ├── schemas/ +│ │ ├── documentation_output.yml +│ │ └── project_analysis.yml +│ ├── scripts/ +│ │ ├── analyze_project.py +│ │ └── generate_html.py +│ └── templates/ +│ └── documentation.html +│ +├── CLAUDE.md # Project instructions +└── .mcp.json # MCP server config +``` + +--- + +### Creating a New Command + +Commands are markdown files that define executable workflows. + +#### File Location +``` +templates/.claude/commands//.md +``` + +#### Command File Format + +```markdown +--- +description: Short description shown in command list +allowed-tools: Read, Write, Edit, Bash, Task, TodoWrite +--- + +# Command Title + +**Input**: "$ARGUMENTS" + +--- + +## PURPOSE + +Explain what this command does and when to use it. + +--- + +## ⛔ CRITICAL RULES + +### MUST DO +1. **MUST** do this thing +2. **MUST** do another thing + +### CANNOT DO +1. **CANNOT** do this +2. **CANNOT** do that + +--- + +## EXECUTION FLOW + +### ═══════════════════════════════════════════════════════════════ +### PHASE 1: Phase Name +### ═══════════════════════════════════════════════════════════════ + +#### 1.1: Step Name +```bash +# Commands to execute +echo "Hello" +``` + +#### 1.2: Another Step + +**Use Task tool with agent:** +``` +Use Task tool with: + subagent_type: "agent-name" + prompt: | + Instructions for the agent... +``` + +--- + +### ═══════════════════════════════════════════════════════════════ +### PHASE 2: Next Phase +### ═══════════════════════════════════════════════════════════════ + +[Continue with more phases...] + +--- + +## USAGE + +```bash +/namespace:command +/namespace:command argument +/namespace:command --flag value +``` +``` + +#### Command Conventions + +| Element | Convention | +|---------|------------| +| Namespace | Lowercase, matches folder name (`eureka`, `workflow`) | +| Command name | Lowercase, matches file name without `.md` | +| Phases | Use `═══` banners with PHASE N headers | +| Steps | Use `####` with numbered steps (1.1, 1.2) | +| Banners | ASCII art boxes for important output | +| Arguments | Reference as `$ARGUMENTS` | + +--- + +### Creating a New Skill + +Skills are packages that bundle agents, schemas, scripts, and templates. + +#### Skill Directory Structure + +``` +skills// +├── skill.yml # REQUIRED: Skill manifest +├── agents/ # REQUIRED: Agent definitions +│ └── .yml +├── schemas/ # OPTIONAL: Data schemas +│ └── .yml +├── scripts/ # OPTIONAL: Python/Bash scripts +│ └── .py +└── templates/ # OPTIONAL: HTML/MD templates + └── .html +``` + +#### skill.yml Format + +```yaml +# Skill manifest +name: skill-name +version: "1.0.0" +description: | + Multi-line description of what this skill does. + +# Activation triggers +triggers: + commands: + - "/namespace:command" + keywords: + - "trigger phrase" + - "another trigger" + +# Components +agents: + - agent-name + +schemas: + - schema-name.yml + +scripts: + - script-name.py + +templates: + - template-name.html + +# Capabilities list +capabilities: + - Capability one + - Capability two + +# Output files +outputs: + primary: + - output-file.md + optional: + - optional-file.md + +# Tool dependencies +dependencies: + required: + - Read tool + - Write tool + optional: + - Bash tool +``` + +--- + +### Creating a New Agent + +Agents are YAML files that define specialized AI personas. + +#### File Location +``` +skills//agents/.yml +``` + +#### Agent File Format + +```yaml +# Agent Definition +name: agent-name +role: Agent Role Title +description: | + Multi-line description of what this agent specializes in + and when it should be used. + +# Tool permissions +allowed_tools: + - Read + - Write + - Edit + - Glob + - Grep + +blocked_tools: + - Task # Prevent sub-agent spawning + - Bash # Prevent shell access + +# File access restrictions +allowed_files: + - "docs/**/*" + - "*.md" + - "package.json" + +# What this agent does +responsibilities: + - First responsibility + - Second responsibility + - Third responsibility + +# What this agent produces +outputs: + - output-file.md + - another-output.yml + +# Restrictions +cannot_do: + - Cannot modify source code + - Cannot run tests + - Cannot deploy + +# Agent-specific configuration +custom_config: + key: value + nested: + setting: value +``` + +#### Agent Naming Conventions + +| Type | Naming Pattern | Examples | +|------|----------------|----------| +| Task agents | `-` | `backend-architect`, `frontend-engineer` | +| Review agents | `-reviewer` | `security-reviewer`, `code-reviewer` | +| Analysis agents | `-analyzer` | `codebase-analyzer`, `dependency-analyzer` | +| Writer agents | `-writer` | `doc-writer`, `test-writer` | + +--- + +### Creating a Schema + +Schemas define the structure of YAML/JSON data files. + +#### File Location +``` +skills//schemas/.yml +``` + +#### Schema File Format + +```yaml +# Schema metadata +version: "1.0" +description: What this schema defines + +# Top-level structure +: + type: object + required: true + fields: + field_name: + type: string|integer|boolean|array|object|enum + required: true|false + description: What this field represents + default: default_value + + enum_field: + type: enum + values: [value1, value2, value3] + + array_field: + type: array + items: + field: type + another: type + min_items: 1 + max_items: 10 + + nested_object: + type: object + fields: + nested_field: + type: string + +# Validation rules +validation_rules: + - name: rule_name + description: What this rule checks + +# Processing instructions +processing: + step_1: + description: First processing step + step_2: + description: Second processing step +``` + +--- + +### Creating a Script + +Scripts automate validation, generation, or transformation tasks. + +#### File Location +``` +skills//scripts/.py +``` + +#### Script Template + +```python +#!/usr/bin/env python3 +""" +Script Name +Brief description of what this script does. +""" + +import os +import sys +import json +from pathlib import Path +from typing import Dict, List, Any, Optional + +# Try optional imports +try: + import yaml +except ImportError: + yaml = None + + +def main_function(input_path: Path, output_path: Optional[Path] = None) -> Dict[str, Any]: + """ + Main processing function. + + Args: + input_path: Path to input file + output_path: Optional path for output + + Returns: + Processing result + """ + # Implementation here + pass + + +def main(): + """CLI entry point.""" + if len(sys.argv) < 2: + print("Usage: script_name.py [output]", file=sys.stderr) + sys.exit(1) + + input_path = Path(sys.argv[1]) + output_path = Path(sys.argv[2]) if len(sys.argv) > 2 else None + + if not input_path.exists(): + print(f"Error: Input not found: {input_path}", file=sys.stderr) + sys.exit(1) + + result = main_function(input_path, output_path) + + # Output result + if output_path: + output_path.write_text(json.dumps(result, indent=2)) + print(f"Output written to: {output_path}") + else: + print(json.dumps(result, indent=2)) + + +if __name__ == '__main__': + main() +``` + +#### Script Conventions + +| Convention | Description | +|------------|-------------| +| Exit codes | `0` = success, `1` = error, `2` = critical | +| Input | Accept file paths as arguments | +| Output | Write to stdout or specified file | +| Dependencies | Handle missing imports gracefully | +| Errors | Print to stderr with clear messages | + +--- + +### Creating a Template + +Templates are HTML/Markdown files with placeholders. + +#### File Location +``` +skills//templates/.html +``` + +#### Placeholder Conventions + +```html + +{{VARIABLE_NAME}} + + + +
default content
+ + + +{{#IF_CONDITION}} + Content shown if condition is true +{{/IF_CONDITION}} + + +{{#EACH_ITEMS}} +
{{ITEM_NAME}}
+{{/EACH_ITEMS}} +``` + +--- + +### Checklist: Creating a New Feature + +When creating a new feature, follow this checklist: + +``` +□ 1. PLAN THE FEATURE + □ Define the purpose and scope + □ Identify target users (engineers/non-engineers/both) + □ List required capabilities + +□ 2. CREATE THE SKILL + □ Create skill directory: skills// + □ Create skill.yml manifest + □ Define triggers and capabilities + +□ 3. CREATE THE AGENT(S) + □ Create agents/.yml + □ Define allowed/blocked tools + □ Define responsibilities and outputs + +□ 4. CREATE SCHEMAS (if needed) + □ Create schemas/.yml + □ Define data structure + □ Add validation rules + +□ 5. CREATE SCRIPTS (if needed) + □ Create scripts/.py + □ Handle CLI arguments + □ Add error handling + +□ 6. CREATE TEMPLATES (if needed) + □ Create templates/.html + □ Add CSS styling + □ Use placeholder conventions + +□ 7. CREATE THE COMMAND + □ Create .claude/commands//.md + □ Add YAML front-matter + □ Define execution phases + □ Add usage examples + +□ 8. UPDATE DOCUMENTATION + □ Update CLAUDE.md + □ Add to Project Structure section + □ Document new commands +``` + +--- + +### Example: Creating a "Report Generator" Feature + +Here's a complete example of creating a new feature: + +#### 1. Create Skill Directory +``` +skills/report-generator/ +├── skill.yml +├── agents/ +│ └── report-writer.yml +├── schemas/ +│ └── report_format.yml +├── scripts/ +│ └── generate_report.py +└── templates/ + └── report.html +``` + +#### 2. skill.yml +```yaml +name: report-generator +version: "1.0.0" +description: Generate project status reports + +triggers: + commands: + - "/eureka:report" + keywords: + - "generate report" + - "status report" + +agents: + - report-writer + +outputs: + primary: + - report.html + - report.md +``` + +#### 3. agents/report-writer.yml +```yaml +name: report-writer +role: Report Generation Specialist +description: Creates project status reports + +allowed_tools: + - Read + - Write + - Glob + - Grep + +responsibilities: + - Analyze project status + - Generate executive summaries + - Create visual charts + +outputs: + - report.html + - report.md +``` + +#### 4. .claude/commands/eureka/report.md +```markdown +--- +description: Generate project status report +allowed-tools: Read, Write, Task, Glob +--- + +# Eureka Report Generator + +**Input**: "$ARGUMENTS" + +## EXECUTION FLOW + +### PHASE 1: Analyze Project +[...] + +### PHASE 2: Generate Report +[...] +``` + +#### 5. Update CLAUDE.md +Add documentation for the new `/eureka:report` command. diff --git a/app/api/apps/[id]/route.ts b/app/api/apps/[id]/route.ts new file mode 100644 index 0000000..3a2b8ff --- /dev/null +++ b/app/api/apps/[id]/route.ts @@ -0,0 +1,102 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { GetAppResponse, GetAppError404, DeleteAppResponse, DeleteAppError404 } from '@/types/api-types'; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { id } = await params; + + const app = await prisma.generatedApp.findUnique({ + where: { id }, + select: { + id: true, + userId: true, + recordingId: true, + title: true, + description: true, + htmlContent: true, + appType: true, + status: true, + createdAt: true, + } + }); + + if (!app) { + const error: GetAppError404 = { error: 'App not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (app.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + const response: GetAppResponse = { + id: app.id, + recordingId: app.recordingId, + title: app.title, + description: app.description || '', + htmlContent: app.htmlContent, + appType: app.appType || '', + status: app.status, + createdAt: app.createdAt.toISOString(), + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Get app error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { id } = await params; + + const app = await prisma.generatedApp.findUnique({ + where: { id }, + select: { userId: true } + }); + + if (!app) { + const error: DeleteAppError404 = { error: 'App not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (app.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + // Delete app + await prisma.generatedApp.delete({ + where: { id } + }); + + const response: DeleteAppResponse = { + success: true, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Delete app error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/apps/generate/route.ts b/app/api/apps/generate/route.ts new file mode 100644 index 0000000..0eb09ff --- /dev/null +++ b/app/api/apps/generate/route.ts @@ -0,0 +1,91 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { GenerateAppRequest, GenerateAppResponse, GenerateAppError400, GenerateAppError404 } from '@/types/api-types'; + +export async function POST(request: NextRequest) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const body: GenerateAppRequest = await request.json(); + + if (!body.recordingId) { + const error: GenerateAppError400 = { error: 'Recording ID is required' }; + return NextResponse.json(error, { status: 400 }); + } + + // Find recording + const recording = await prisma.recording.findUnique({ + where: { id: body.recordingId }, + select: { userId: true, summary: true, title: true } + }); + + if (!recording) { + const error: GenerateAppError404 = { error: 'Recording not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (recording.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + // Check if summary exists + if (!recording.summary) { + const error: GenerateAppError400 = { error: 'Recording summary not available' }; + return NextResponse.json(error, { status: 400 }); + } + + // TODO: Call AI to generate app from summary + // For now, create a placeholder app + const htmlContent = ` + + + + Generated App + + + +

Generated App

+

This app was generated from: ${recording.title}

+

Summary: ${recording.summary}

+ + + `.trim(); + + // Create generated app + const generatedApp = await prisma.generatedApp.create({ + data: { + userId: user.id, + recordingId: body.recordingId, + title: `App from ${recording.title}`, + description: 'Auto-generated application', + htmlContent, + appType: 'generated', + status: 'completed', + }, + select: { + id: true, + recordingId: true, + status: true, + } + }); + + const response: GenerateAppResponse = { + id: generatedApp.id, + recordingId: generatedApp.recordingId, + status: generatedApp.status, + }; + + return NextResponse.json(response, { status: 201 }); + } catch (error) { + console.error('Generate app error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/apps/route.ts b/app/api/apps/route.ts new file mode 100644 index 0000000..4821e17 --- /dev/null +++ b/app/api/apps/route.ts @@ -0,0 +1,54 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { ListAppsResponse } from '@/types/api-types'; + +export async function GET(request: NextRequest) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { searchParams } = new URL(request.url); + const limit = parseInt(searchParams.get('limit') || '50'); + const offset = parseInt(searchParams.get('offset') || '0'); + + const [apps, total] = await Promise.all([ + prisma.generatedApp.findMany({ + where: { userId: user.id }, + orderBy: { createdAt: 'desc' }, + skip: offset, + take: limit, + select: { + id: true, + title: true, + description: true, + appType: true, + status: true, + createdAt: true, + } + }), + prisma.generatedApp.count({ + where: { userId: user.id } + }) + ]); + + const response: ListAppsResponse = { + apps: apps.map(app => ({ + id: app.id, + title: app.title, + description: app.description || undefined, + appType: app.appType || undefined, + status: app.status, + createdAt: app.createdAt.toISOString(), + })), + total, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('List apps error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts new file mode 100644 index 0000000..0a50c0f --- /dev/null +++ b/app/api/auth/login/route.ts @@ -0,0 +1,61 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { cookies } from 'next/headers'; +import prisma from '@/lib/prisma'; +import { verifyPassword, generateToken } from '@/lib/auth'; +import type { LoginUserRequest, LoginUserResponse, LoginUserError401 } from '@/types/api-types'; + +export async function POST(request: NextRequest) { + try { + const body: LoginUserRequest = await request.json(); + + // Validate required fields + if (!body.email || !body.password) { + const error: LoginUserError401 = { error: 'Email and password are required' }; + return NextResponse.json(error, { status: 401 }); + } + + // Find user by email + const user = await prisma.user.findUnique({ + where: { email: body.email } + }); + + if (!user) { + const error: LoginUserError401 = { error: 'Invalid email or password' }; + return NextResponse.json(error, { status: 401 }); + } + + // Verify password + const isValidPassword = await verifyPassword(body.password, user.passwordHash); + + if (!isValidPassword) { + const error: LoginUserError401 = { error: 'Invalid email or password' }; + return NextResponse.json(error, { status: 401 }); + } + + // Generate JWT token + const token = generateToken(user.id); + + // Set cookie + const cookieStore = await cookies(); + cookieStore.set('auth-token', token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 60 * 60 * 24 * 7, // 7 days + }); + + const response: LoginUserResponse = { + user: { + id: user.id, + email: user.email, + name: user.name, + }, + token, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Login error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts new file mode 100644 index 0000000..de32d67 --- /dev/null +++ b/app/api/auth/logout/route.ts @@ -0,0 +1,19 @@ +import { NextResponse } from 'next/server'; +import { cookies } from 'next/headers'; +import type { LogoutUserResponse } from '@/types/api-types'; + +export async function POST() { + try { + const cookieStore = await cookies(); + cookieStore.delete('auth-token'); + + const response: LogoutUserResponse = { + success: true, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Logout error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/auth/me/route.ts b/app/api/auth/me/route.ts new file mode 100644 index 0000000..fe74dee --- /dev/null +++ b/app/api/auth/me/route.ts @@ -0,0 +1,26 @@ +import { NextResponse } from 'next/server'; +import { getCurrentUser } from '@/lib/auth'; +import type { GetCurrentUserResponse, GetCurrentUserError401 } from '@/types/api-types'; + +export async function GET() { + try { + const user = await getCurrentUser(); + + if (!user) { + const error: GetCurrentUserError401 = { error: 'Not authenticated' }; + return NextResponse.json(error, { status: 401 }); + } + + const response: GetCurrentUserResponse = { + id: user.id, + email: user.email, + name: user.name, + createdAt: user.createdAt.toISOString(), + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Get current user error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts new file mode 100644 index 0000000..6475d7e --- /dev/null +++ b/app/api/auth/register/route.ts @@ -0,0 +1,68 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { hashPassword, generateToken } from '@/lib/auth'; +import type { RegisterUserRequest, RegisterUserResponse, RegisterUserError400, RegisterUserError409 } from '@/types/api-types'; + +export async function POST(request: NextRequest) { + try { + const body: RegisterUserRequest = await request.json(); + + // Validate required fields + if (!body.email || !body.name || !body.password) { + const error: RegisterUserError400 = { error: 'Email, name, and password are required' }; + return NextResponse.json(error, { status: 400 }); + } + + // Validate email format + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(body.email)) { + const error: RegisterUserError400 = { error: 'Invalid email format' }; + return NextResponse.json(error, { status: 400 }); + } + + // Validate password length + if (body.password.length < 8) { + const error: RegisterUserError400 = { error: 'Password must be at least 8 characters' }; + return NextResponse.json(error, { status: 400 }); + } + + // Check if email already exists + const existingUser = await prisma.user.findUnique({ + where: { email: body.email } + }); + + if (existingUser) { + const error: RegisterUserError409 = { error: 'Email already registered' }; + return NextResponse.json(error, { status: 409 }); + } + + // Hash password and create user + const passwordHash = await hashPassword(body.password); + + const user = await prisma.user.create({ + data: { + email: body.email, + name: body.name, + passwordHash, + }, + select: { + id: true, + email: true, + name: true, + createdAt: true, + } + }); + + const response: RegisterUserResponse = { + id: user.id, + email: user.email, + name: user.name, + createdAt: user.createdAt.toISOString(), + }; + + return NextResponse.json(response, { status: 201 }); + } catch (error) { + console.error('Registration error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/recordings/[id]/route.ts b/app/api/recordings/[id]/route.ts new file mode 100644 index 0000000..13a540e --- /dev/null +++ b/app/api/recordings/[id]/route.ts @@ -0,0 +1,104 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { GetRecordingResponse, GetRecordingError404, DeleteRecordingResponse, DeleteRecordingError404 } from '@/types/api-types'; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { id } = await params; + + const recording = await prisma.recording.findUnique({ + where: { id }, + select: { + id: true, + userId: true, + title: true, + audioFilePath: true, + duration: true, + transcript: true, + summary: true, + isTranscribing: true, + createdAt: true, + } + }); + + if (!recording) { + const error: GetRecordingError404 = { error: 'Recording not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (recording.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + const response: GetRecordingResponse = { + id: recording.id, + title: recording.title, + audioFilePath: recording.audioFilePath, + duration: recording.duration, + transcript: recording.transcript || '', + summary: recording.summary || '', + isTranscribing: recording.isTranscribing, + createdAt: recording.createdAt.toISOString(), + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Get recording error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { id } = await params; + + const recording = await prisma.recording.findUnique({ + where: { id }, + select: { userId: true, audioFilePath: true } + }); + + if (!recording) { + const error: DeleteRecordingError404 = { error: 'Recording not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (recording.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + // Delete recording + await prisma.recording.delete({ + where: { id } + }); + + // TODO: Delete audio file from MinIO/S3 + + const response: DeleteRecordingResponse = { + success: true, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Delete recording error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/recordings/[id]/summarize/route.ts b/app/api/recordings/[id]/summarize/route.ts new file mode 100644 index 0000000..1e677ce --- /dev/null +++ b/app/api/recordings/[id]/summarize/route.ts @@ -0,0 +1,59 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { SummarizeRecordingResponse, SummarizeRecordingError400, SummarizeRecordingError404 } from '@/types/api-types'; + +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { id } = await params; + + const recording = await prisma.recording.findUnique({ + where: { id }, + select: { userId: true, transcript: true } + }); + + if (!recording) { + const error: SummarizeRecordingError404 = { error: 'Recording not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (recording.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + // Check if transcript exists + if (!recording.transcript) { + const error: SummarizeRecordingError400 = { error: 'Transcript not available' }; + return NextResponse.json(error, { status: 400 }); + } + + // TODO: Call Gemini API to generate summary + // For now, we'll create a placeholder summary + const summary = `Summary of the recording (placeholder)`; + + // Update recording with summary + await prisma.recording.update({ + where: { id }, + data: { summary } + }); + + const response: SummarizeRecordingResponse = { + recordingId: id, + summary, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Summarize recording error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/recordings/[id]/transcribe/route.ts b/app/api/recordings/[id]/transcribe/route.ts new file mode 100644 index 0000000..4da527c --- /dev/null +++ b/app/api/recordings/[id]/transcribe/route.ts @@ -0,0 +1,52 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { TranscribeRecordingResponse, TranscribeRecordingError404 } from '@/types/api-types'; + +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { id } = await params; + + const recording = await prisma.recording.findUnique({ + where: { id }, + select: { userId: true, audioFilePath: true } + }); + + if (!recording) { + const error: TranscribeRecordingError404 = { error: 'Recording not found' }; + return NextResponse.json(error, { status: 404 }); + } + + // Check ownership + if (recording.userId !== user.id) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + // Set transcribing flag + await prisma.recording.update({ + where: { id }, + data: { isTranscribing: true } + }); + + // TODO: Trigger Whisper STT transcription job + // For now, we'll just set the flag and return + + const response: TranscribeRecordingResponse = { + recordingId: id, + isTranscribing: true, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('Transcribe recording error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/api/recordings/route.ts b/app/api/recordings/route.ts new file mode 100644 index 0000000..8e55fe8 --- /dev/null +++ b/app/api/recordings/route.ts @@ -0,0 +1,125 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import { getCurrentUser } from '@/lib/auth'; +import type { ListRecordingsResponse, CreateRecordingResponse, CreateRecordingError400 } from '@/types/api-types'; + +export async function GET(request: NextRequest) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { searchParams } = new URL(request.url); + const limit = parseInt(searchParams.get('limit') || '50'); + const offset = parseInt(searchParams.get('offset') || '0'); + + const [recordings, total] = await Promise.all([ + prisma.recording.findMany({ + where: { userId: user.id }, + orderBy: { createdAt: 'desc' }, + skip: offset, + take: limit, + select: { + id: true, + title: true, + audioFilePath: true, + duration: true, + transcript: true, + summary: true, + isTranscribing: true, + createdAt: true, + updatedAt: true, + } + }), + prisma.recording.count({ + where: { userId: user.id } + }) + ]); + + const response: ListRecordingsResponse = { + recordings: recordings.map(rec => ({ + id: rec.id, + title: rec.title, + audioFilePath: rec.audioFilePath, + duration: rec.duration, + transcript: rec.transcript || undefined, + summary: rec.summary || undefined, + isTranscribing: rec.isTranscribing, + createdAt: rec.createdAt.toISOString(), + })), + total, + }; + + return NextResponse.json(response, { status: 200 }); + } catch (error) { + console.error('List recordings error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +export async function POST(request: NextRequest) { + try { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const formData = await request.formData(); + const audioFile = formData.get('audio') as File | null; + const title = formData.get('title') as string | null; + const durationStr = formData.get('duration') as string | null; + + // Validate required fields + if (!audioFile || !durationStr) { + const error: CreateRecordingError400 = { error: 'Audio file and duration are required' }; + return NextResponse.json(error, { status: 400 }); + } + + const duration = parseInt(durationStr); + if (isNaN(duration) || duration < 1) { + const error: CreateRecordingError400 = { error: 'Invalid duration' }; + return NextResponse.json(error, { status: 400 }); + } + + // Generate filename + const timestamp = Date.now(); + const extension = audioFile.name.split('.').pop() || 'webm'; + const filename = `${user.id}/${timestamp}.${extension}`; + const audioFilePath = `recordings/${filename}`; + + // TODO: Upload to MinIO/S3 storage + // For now, we'll just store the path + + // Create recording + const recording = await prisma.recording.create({ + data: { + userId: user.id, + title: title || `Recording ${new Date().toISOString()}`, + audioFilePath, + duration, + isTranscribing: false, + }, + select: { + id: true, + title: true, + audioFilePath: true, + duration: true, + createdAt: true, + } + }); + + const response: CreateRecordingResponse = { + id: recording.id, + title: recording.title, + audioFilePath: recording.audioFilePath, + duration: recording.duration, + createdAt: recording.createdAt.toISOString(), + }; + + return NextResponse.json(response, { status: 201 }); + } catch (error) { + console.error('Create recording error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/app/apps/[id]/page.tsx b/app/apps/[id]/page.tsx new file mode 100644 index 0000000..5498835 --- /dev/null +++ b/app/apps/[id]/page.tsx @@ -0,0 +1,115 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter, useParams } from 'next/navigation'; +import Header from '../../components/Header'; +import Sidebar from '../../components/Sidebar'; +import AppIframeViewer from '../../components/AppIframeViewer'; +import type { User, GeneratedApp } from '@/types'; + +export default function AppDetailPage() { + const [user, setUser] = useState(null); + const [app, setApp] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const router = useRouter(); + const params = useParams(); + const id = params?.id as string; + + useEffect(() => { + // Fetch current user + fetch('/api/auth/me') + .then(res => { + if (!res.ok) { + router.push('/login'); + return null; + } + return res.json(); + }) + .then(data => { + if (data) setUser(data); + }) + .catch(() => router.push('/login')); + + // Fetch app + if (id) { + fetch(`/api/apps/${id}`) + .then(res => { + if (!res.ok) throw new Error('App not found'); + return res.json(); + }) + .then(data => { + setApp(data); + }) + .catch(() => router.push('/apps')) + .finally(() => setIsLoading(false)); + } + }, [id, router]); + + const handleLoadComplete = () => { + console.log('App iframe loaded'); + }; + + if (isLoading || !app) { + return ( +
+
+
+ ); + } + + return ( +
+ +
+
+
+
+
+

+ {app.title} +

+ {app.description && ( +

{app.description}

+ )} +
+ {app.appType && ( + + {app.appType} + + )} + + {app.status} + +
+
+ + {app.status === 'completed' && ( + + )} + + {app.status === 'generating' && ( +
+
+

Generating app...

+
+ )} + + {app.status === 'failed' && ( +
+

App generation failed

+
+ )} +
+
+
+
+ ); +} diff --git a/app/apps/page.tsx b/app/apps/page.tsx new file mode 100644 index 0000000..99cb338 --- /dev/null +++ b/app/apps/page.tsx @@ -0,0 +1,105 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import Header from '../components/Header'; +import Sidebar from '../components/Sidebar'; +import AppGallery from '../components/AppGallery'; +import type { User, GeneratedApp } from '@/types'; + +export default function AppsPage() { + const [user, setUser] = useState(null); + const [apps, setApps] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [filterType, setFilterType] = useState('all'); + const router = useRouter(); + + useEffect(() => { + // Fetch current user + fetch('/api/auth/me') + .then(res => { + if (!res.ok) { + router.push('/login'); + return null; + } + return res.json(); + }) + .then(data => { + if (data) setUser(data); + }) + .catch(() => router.push('/login')); + + // Fetch apps + fetch('/api/apps') + .then(res => res.json()) + .then(data => { + if (data.apps) { + setApps(data.apps); + } + }) + .catch(console.error) + .finally(() => setIsLoading(false)); + }, [router]); + + const handleSelectApp = (id: string) => { + router.push(`/apps/${id}`); + }; + + const handleDeleteApp = async (id: string) => { + try { + const response = await fetch(`/api/apps/${id}`, { + method: 'DELETE', + }); + + if (response.ok) { + setApps(apps.filter(a => a.id !== id)); + } + } catch (error) { + console.error('Failed to delete app:', error); + } + }; + + const filteredApps = filterType === 'all' + ? apps + : apps.filter(app => app.appType === filterType); + + const appTypes = Array.from(new Set(apps.map(a => a.appType).filter(Boolean))); + + return ( +
+ +
+
+
+
+

Generated Apps

+ + {appTypes.length > 0 && ( +
+ + +
+ )} + + +
+
+
+
+ ); +} diff --git a/app/components/AppCard.tsx b/app/components/AppCard.tsx new file mode 100644 index 0000000..303ee2e --- /dev/null +++ b/app/components/AppCard.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { useState } from 'react'; +import type { AppCardProps } from '@/types/component-props'; + +export default function AppCard({ + app, + showActions = true, + onClick, + onDelete, +}: AppCardProps) { + const [isDeleting, setIsDeleting] = useState(false); + + const handleDelete = async (e: React.MouseEvent) => { + e.stopPropagation(); + if (!confirm('Delete this app?')) return; + + setIsDeleting(true); + onDelete?.(app.id ?? ''); + }; + + const statusColors = { + generating: 'bg-yellow-100 text-yellow-800', + completed: 'bg-green-100 text-green-800', + failed: 'bg-red-100 text-red-800', + }; + + return ( +
onClick?.(app.id ?? '')} + className="bg-white border rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer" + > +
+
+
+

{app.title}

+ + {app.status} + +
+ {app.appType && ( + + {app.appType} + + )} + {app.description && ( +

+ {app.description} +

+ )} + {app.createdAt && ( +

+ Created: {new Date(app.createdAt).toLocaleDateString()} +

+ )} +
+ {showActions && ( + + )} +
+
+ ); +} diff --git a/app/components/AppGallery.tsx b/app/components/AppGallery.tsx new file mode 100644 index 0000000..cd8fcec --- /dev/null +++ b/app/components/AppGallery.tsx @@ -0,0 +1,42 @@ +'use client'; + +import type { AppGalleryProps } from '@/types/component-props'; +import AppCard from './AppCard'; + +export default function AppGallery({ + apps, + isLoading = false, + onSelectApp, +}: AppGalleryProps) { + if (isLoading) { + return ( +
+
+ Loading apps... +
+ ); + } + + if (apps.length === 0) { + return ( +
+

No apps generated yet

+

+ Record a voice note and say "create an app" to generate your first application +

+
+ ); + } + + return ( +
+ {apps.map((app) => ( + + ))} +
+ ); +} diff --git a/app/components/AppIframeViewer.tsx b/app/components/AppIframeViewer.tsx new file mode 100644 index 0000000..369e4b4 --- /dev/null +++ b/app/components/AppIframeViewer.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { useState, useRef, useEffect } from 'react'; +import type { AppIframeViewerProps } from '@/types/component-props'; + +export default function AppIframeViewer({ + htmlContent, + title, + onLoadComplete, +}: AppIframeViewerProps) { + const [isLoading, setIsLoading] = useState(true); + const iframeRef = useRef(null); + + useEffect(() => { + if (iframeRef.current) { + const iframe = iframeRef.current; + const handleLoad = () => { + setIsLoading(false); + onLoadComplete?.(); + }; + + iframe.addEventListener('load', handleLoad); + return () => iframe.removeEventListener('load', handleLoad); + } + }, [onLoadComplete]); + + return ( +
+
+

{title}

+ {isLoading && ( + Loading... + )} +
+ +
+ {isLoading && ( +
+
+
+ )} +