moai-adk 0.9.0__py3-none-any.whl → 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/cli/commands/init.py +14 -2
- moai_adk/cli/commands/update.py +214 -56
- moai_adk/core/issue_creator.py +2 -2
- moai_adk/core/project/detector.py +201 -12
- moai_adk/core/project/initializer.py +62 -1
- moai_adk/core/project/phase_executor.py +48 -6
- moai_adk/core/tags/ci_validator.py +34 -4
- moai_adk/core/tags/pre_commit_validator.py +40 -2
- moai_adk/core/tags/reporter.py +2 -3
- moai_adk/core/tags/validator.py +1 -1
- moai_adk/core/template_engine.py +20 -5
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +1 -1
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +2 -2
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +76 -3
- moai_adk/templates/.claude/agents/alfred/project-manager.md +49 -10
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +3 -3
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +108 -3
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +74 -0
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +107 -5
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +2 -2
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +465 -129
- moai_adk/templates/.claude/commands/alfred/1-plan.md +139 -65
- moai_adk/templates/.claude/commands/alfred/2-run.md +214 -50
- moai_adk/templates/.claude/commands/alfred/3-sync.md +372 -46
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +1 -1
- moai_adk/templates/.claude/hooks/alfred/core/project.py +25 -27
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +29 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +10 -18
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +2 -2
- moai_adk/templates/.claude/hooks/alfred/shared/core/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +40 -41
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +55 -23
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +132 -3
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +9 -10
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +3 -6
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +14 -22
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/settings.json +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/SKILL.md +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
- moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
- moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
- moai_adk/templates/.git-hooks/pre-push +143 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +182 -25
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +35 -29
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +76 -7
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +208 -41
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.moai/config.json +3 -1
- moai_adk/templates/CLAUDE.md +940 -45
- moai_adk/templates/workflows/go-tag-validation.yml +30 -0
- moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
- moai_adk/templates/workflows/python-tag-validation.yml +42 -0
- moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
- moai_adk/utils/banner.py +5 -5
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1166 -455
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +169 -109
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -209
- moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +0 -102
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -330
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/README.md +0 -256
- moai_adk/templates/__init__.py +0 -2
- /moai_adk/templates/{.moai/memory/ISSUE-LABEL-MAPPING.md → .claude/skills/moai-alfred-issue-labels/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/README.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/reference.md +0 -0
- /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# Alfred Development Guide - Examples
|
|
2
|
+
|
|
3
|
+
## Example 1: Complete SPEC-First TDD Workflow for User Authentication
|
|
4
|
+
|
|
5
|
+
### Phase 1: Write SPEC (30 minutes)
|
|
6
|
+
|
|
7
|
+
**File**: `.moai/specs/SPEC-AUTH-001/spec.md`
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
---
|
|
11
|
+
id: AUTH-001
|
|
12
|
+
title: "User Authentication System"
|
|
13
|
+
version: 0.1.0
|
|
14
|
+
status: active
|
|
15
|
+
created: 2025-11-03
|
|
16
|
+
updated: 2025-11-03
|
|
17
|
+
author: @GOOS
|
|
18
|
+
priority: high
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# User Authentication SPEC
|
|
22
|
+
|
|
23
|
+
@SPEC:AUTH-001
|
|
24
|
+
|
|
25
|
+
## Ubiquitous Requirements
|
|
26
|
+
- The system shall provide user authentication via email and password.
|
|
27
|
+
- The system shall validate email format (RFC 5322) before storage.
|
|
28
|
+
- The system shall hash passwords using bcrypt (≥12 rounds).
|
|
29
|
+
|
|
30
|
+
## Event-driven Requirements
|
|
31
|
+
- WHEN a user submits signup form, the system shall create user account.
|
|
32
|
+
- WHEN email verification link is clicked, the system shall activate account.
|
|
33
|
+
- WHEN login fails 3 times, the system shall lock account for 1 hour.
|
|
34
|
+
|
|
35
|
+
## State-driven Requirements
|
|
36
|
+
- WHILE user is authenticated, the system shall allow access to protected resources.
|
|
37
|
+
- WHILE session is active, the system shall maintain JWT token validity.
|
|
38
|
+
|
|
39
|
+
## Optional Features
|
|
40
|
+
- WHERE 2FA is enabled, the system may require additional verification.
|
|
41
|
+
|
|
42
|
+
## Constraints
|
|
43
|
+
- IF password is invalid, the system shall return 401 Unauthorized.
|
|
44
|
+
- The system shall NOT store plaintext passwords.
|
|
45
|
+
- The system shall enforce 8-character minimum password length.
|
|
46
|
+
|
|
47
|
+
## HISTORY
|
|
48
|
+
|
|
49
|
+
### v0.1.0 (2025-11-03)
|
|
50
|
+
- Initial draft with core auth requirements
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Phase 2: TDD Implementation (2 hours)
|
|
54
|
+
|
|
55
|
+
**RED: Write failing tests first**
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
# tests/test_auth.py
|
|
59
|
+
import pytest
|
|
60
|
+
from src.auth import signup, login, verify_email # @TEST:AUTH-001
|
|
61
|
+
from src.models import User
|
|
62
|
+
|
|
63
|
+
class TestSignup:
|
|
64
|
+
"""SPEC: User signup and verification workflow"""
|
|
65
|
+
|
|
66
|
+
def test_signup_creates_user(self): # @TEST:AUTH-001
|
|
67
|
+
"""SPEC: The system shall create user account."""
|
|
68
|
+
response = signup(email="user@example.com", password="securePass123")
|
|
69
|
+
|
|
70
|
+
assert response["status"] == "created"
|
|
71
|
+
user = User.find_by_email("user@example.com")
|
|
72
|
+
assert user is not None
|
|
73
|
+
assert user.verified is False
|
|
74
|
+
|
|
75
|
+
def test_invalid_email_rejected(self): # @TEST:AUTH-001
|
|
76
|
+
"""SPEC: The system shall validate email format."""
|
|
77
|
+
with pytest.raises(ValueError):
|
|
78
|
+
signup(email="invalid-email", password="securePass123")
|
|
79
|
+
|
|
80
|
+
def test_weak_password_rejected(self): # @TEST:AUTH-001
|
|
81
|
+
"""SPEC: Enforce 8-character minimum password length."""
|
|
82
|
+
with pytest.raises(ValueError):
|
|
83
|
+
signup(email="user@example.com", password="short")
|
|
84
|
+
|
|
85
|
+
class TestLogin:
|
|
86
|
+
"""SPEC: User login and session management"""
|
|
87
|
+
|
|
88
|
+
def test_successful_login(self): # @TEST:AUTH-001
|
|
89
|
+
"""SPEC: The system shall authenticate valid credentials."""
|
|
90
|
+
user = create_test_user("user@example.com", "securePass123", verified=True)
|
|
91
|
+
|
|
92
|
+
token = login(email="user@example.com", password="securePass123")
|
|
93
|
+
|
|
94
|
+
assert token is not None
|
|
95
|
+
assert isinstance(token, str)
|
|
96
|
+
|
|
97
|
+
def test_failed_login_locks_account(self): # @TEST:AUTH-001
|
|
98
|
+
"""SPEC: WHEN login fails 3 times, lock account."""
|
|
99
|
+
user = create_test_user("user@example.com", "securePass123", verified=True)
|
|
100
|
+
|
|
101
|
+
for i in range(3):
|
|
102
|
+
with pytest.raises(Exception):
|
|
103
|
+
login(email="user@example.com", password="wrongPassword")
|
|
104
|
+
|
|
105
|
+
assert user.locked is True
|
|
106
|
+
assert user.locked_until is not None
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**GREEN: Minimal implementation that passes tests**
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# src/auth.py
|
|
113
|
+
from datetime import datetime, timedelta # @CODE:AUTH-001
|
|
114
|
+
import bcrypt
|
|
115
|
+
import jwt
|
|
116
|
+
from src.models import User
|
|
117
|
+
from src.email import send_verification_email
|
|
118
|
+
|
|
119
|
+
def signup(email: str, password: str) -> dict: # @CODE:AUTH-001
|
|
120
|
+
"""Create new user account with email verification."""
|
|
121
|
+
# Validate inputs
|
|
122
|
+
if "@" not in email:
|
|
123
|
+
raise ValueError("Invalid email")
|
|
124
|
+
if len(password) < 8:
|
|
125
|
+
raise ValueError("Password too short")
|
|
126
|
+
|
|
127
|
+
# Hash password
|
|
128
|
+
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(12))
|
|
129
|
+
|
|
130
|
+
# Create user
|
|
131
|
+
user = User.create(email=email, password_hash=hashed, verified=False)
|
|
132
|
+
|
|
133
|
+
# Send verification email
|
|
134
|
+
token = generate_verification_token(user.id)
|
|
135
|
+
send_verification_email(email, token)
|
|
136
|
+
|
|
137
|
+
return {"status": "created", "user_id": user.id}
|
|
138
|
+
|
|
139
|
+
def login(email: str, password: str) -> str: # @CODE:AUTH-001
|
|
140
|
+
"""Authenticate user and return JWT token."""
|
|
141
|
+
user = User.find_by_email(email)
|
|
142
|
+
if not user:
|
|
143
|
+
raise ValueError("Invalid credentials")
|
|
144
|
+
|
|
145
|
+
if user.locked:
|
|
146
|
+
raise ValueError("Account locked")
|
|
147
|
+
|
|
148
|
+
# Verify password
|
|
149
|
+
if not bcrypt.checkpw(password.encode(), user.password_hash):
|
|
150
|
+
user.failed_attempts += 1
|
|
151
|
+
if user.failed_attempts >= 3:
|
|
152
|
+
user.locked = True
|
|
153
|
+
user.locked_until = datetime.now() + timedelta(hours=1)
|
|
154
|
+
user.save()
|
|
155
|
+
raise ValueError("Invalid credentials")
|
|
156
|
+
|
|
157
|
+
# Reset attempts and create token
|
|
158
|
+
user.failed_attempts = 0
|
|
159
|
+
token = jwt.encode({"user_id": user.id, "exp": datetime.utcnow() + timedelta(hours=1)}, "SECRET_KEY")
|
|
160
|
+
user.save()
|
|
161
|
+
|
|
162
|
+
return token
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**REFACTOR: Improve code quality**
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# src/auth.py (refactored)
|
|
169
|
+
from datetime import datetime, timedelta # @CODE:AUTH-001
|
|
170
|
+
import bcrypt
|
|
171
|
+
import jwt
|
|
172
|
+
from typing import Dict, Optional
|
|
173
|
+
from src.models import User
|
|
174
|
+
from src.email import send_verification_email
|
|
175
|
+
from src.config import Settings
|
|
176
|
+
|
|
177
|
+
MAX_FAILED_ATTEMPTS = 3 # @CODE:AUTH-001
|
|
178
|
+
LOCKOUT_DURATION_HOURS = 1 # @CODE:AUTH-001
|
|
179
|
+
MIN_PASSWORD_LENGTH = 8 # @CODE:AUTH-001
|
|
180
|
+
|
|
181
|
+
def _validate_email(email: str) -> None:
|
|
182
|
+
"""Validate email format."""
|
|
183
|
+
if "@" not in email or "." not in email.split("@")[1]:
|
|
184
|
+
raise ValueError("Invalid email format")
|
|
185
|
+
|
|
186
|
+
def _validate_password(password: str) -> None:
|
|
187
|
+
"""Validate password strength."""
|
|
188
|
+
if len(password) < MIN_PASSWORD_LENGTH:
|
|
189
|
+
raise ValueError(f"Password must be at least {MIN_PASSWORD_LENGTH} characters")
|
|
190
|
+
|
|
191
|
+
def signup(email: str, password: str) -> Dict[str, any]: # @CODE:AUTH-001
|
|
192
|
+
"""Create new user account with email verification.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
email: User email address
|
|
196
|
+
password: User password (min 8 chars)
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Dictionary with status and user_id
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
ValueError: If email or password invalid
|
|
203
|
+
"""
|
|
204
|
+
_validate_email(email)
|
|
205
|
+
_validate_password(password)
|
|
206
|
+
|
|
207
|
+
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(12))
|
|
208
|
+
user = User.create(email=email, password_hash=hashed, verified=False)
|
|
209
|
+
|
|
210
|
+
token = jwt.encode({"user_id": user.id, "type": "verify"}, Settings.SECRET_KEY)
|
|
211
|
+
send_verification_email(email, token)
|
|
212
|
+
|
|
213
|
+
return {"status": "created", "user_id": user.id}
|
|
214
|
+
|
|
215
|
+
def login(email: str, password: str) -> str: # @CODE:AUTH-001
|
|
216
|
+
"""Authenticate user and return JWT token."""
|
|
217
|
+
user = User.find_by_email(email)
|
|
218
|
+
if not user:
|
|
219
|
+
raise ValueError("Invalid credentials")
|
|
220
|
+
|
|
221
|
+
if user.is_locked():
|
|
222
|
+
raise ValueError("Account temporarily locked")
|
|
223
|
+
|
|
224
|
+
if not bcrypt.checkpw(password.encode(), user.password_hash):
|
|
225
|
+
_handle_failed_login(user)
|
|
226
|
+
raise ValueError("Invalid credentials")
|
|
227
|
+
|
|
228
|
+
_reset_login_attempts(user)
|
|
229
|
+
token = jwt.encode({
|
|
230
|
+
"user_id": user.id,
|
|
231
|
+
"exp": datetime.utcnow() + timedelta(hours=1)
|
|
232
|
+
}, Settings.SECRET_KEY)
|
|
233
|
+
|
|
234
|
+
return token
|
|
235
|
+
|
|
236
|
+
def _handle_failed_login(user: User) -> None:
|
|
237
|
+
"""Track failed login attempts."""
|
|
238
|
+
user.failed_attempts += 1
|
|
239
|
+
if user.failed_attempts >= MAX_FAILED_ATTEMPTS:
|
|
240
|
+
user.locked_until = datetime.now() + timedelta(hours=LOCKOUT_DURATION_HOURS)
|
|
241
|
+
user.save()
|
|
242
|
+
|
|
243
|
+
def _reset_login_attempts(user: User) -> None:
|
|
244
|
+
"""Reset failed attempt counter on successful login."""
|
|
245
|
+
user.failed_attempts = 0
|
|
246
|
+
user.locked_until = None
|
|
247
|
+
user.save()
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Phase 3: Sync Documentation (1 hour)
|
|
251
|
+
|
|
252
|
+
**Update SPEC status**:
|
|
253
|
+
|
|
254
|
+
```yaml
|
|
255
|
+
status: completed
|
|
256
|
+
version: 1.0.0
|
|
257
|
+
updated: 2025-11-03
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Update README.md**:
|
|
261
|
+
|
|
262
|
+
```markdown
|
|
263
|
+
## Authentication
|
|
264
|
+
|
|
265
|
+
User authentication system with email verification and brute-force protection.
|
|
266
|
+
|
|
267
|
+
- Email signup with verification
|
|
268
|
+
- JWT-based sessions
|
|
269
|
+
- Automatic lockout after 3 failed attempts
|
|
270
|
+
- See SPEC-AUTH-001 for requirements
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Generate sync report**:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
rg '@(SPEC|TEST|CODE|DOC):AUTH-001' -n .moai/specs/ tests/ src/ docs/
|
|
277
|
+
|
|
278
|
+
# Output:
|
|
279
|
+
# .moai/specs/SPEC-AUTH-001/spec.md:@SPEC:AUTH-001
|
|
280
|
+
# tests/test_auth.py:@TEST:AUTH-001 (4 matches)
|
|
281
|
+
# src/auth.py:@CODE:AUTH-001 (5 matches)
|
|
282
|
+
# README.md:SPEC-AUTH-001
|
|
283
|
+
|
|
284
|
+
✅ TAG chain verified: SPEC→TEST→CODE→DOC
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Example 2: Context Engineering in Action
|
|
288
|
+
|
|
289
|
+
### `/alfred:1-plan` Command
|
|
290
|
+
|
|
291
|
+
Only load:
|
|
292
|
+
```
|
|
293
|
+
✅ .moai/project/product.md (150 KB)
|
|
294
|
+
✅ .moai/project/structure.md (50 KB)
|
|
295
|
+
|
|
296
|
+
❌ Skip: All SPEC files, analysis, reports
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Context saved: ~5 MB
|
|
300
|
+
|
|
301
|
+
### `/alfred:2-run` Command
|
|
302
|
+
|
|
303
|
+
Load only:
|
|
304
|
+
```
|
|
305
|
+
✅ .moai/specs/SPEC-AUTH-001/spec.md (20 KB)
|
|
306
|
+
✅ development-guide.md (14 KB) ← This Skill!
|
|
307
|
+
|
|
308
|
+
❌ Skip: Other SPECs, unrelated docs, analysis
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Context saved: ~200 MB
|
|
312
|
+
|
|
313
|
+
## Example 3: TRUST 5 Validation Checklist
|
|
314
|
+
|
|
315
|
+
```markdown
|
|
316
|
+
# AUTH-001 Implementation Verification
|
|
317
|
+
|
|
318
|
+
## T – Test-Driven (SPEC-Aligned)
|
|
319
|
+
- [x] SPEC written with EARS format (5 patterns)
|
|
320
|
+
- [x] RED: Tests written first, all fail
|
|
321
|
+
- [x] GREEN: Minimal code passes all tests
|
|
322
|
+
- [x] REFACTOR: Code improved, tests still pass
|
|
323
|
+
- [x] @TAG chain complete: SPEC→TEST(4)→CODE(5)→README
|
|
324
|
+
|
|
325
|
+
## R – Readable
|
|
326
|
+
- [x] Function names: signup(), login(), _validate_email()
|
|
327
|
+
- [x] Variables: email, password, hashed, token
|
|
328
|
+
- [x] Comments explain WHY: "bcrypt 12 rounds for security"
|
|
329
|
+
- [x] Docstrings for public functions
|
|
330
|
+
- [x] README updated
|
|
331
|
+
|
|
332
|
+
## U – Unified
|
|
333
|
+
- [x] Naming: snake_case for functions, UPPER_CASE for constants
|
|
334
|
+
- [x] Error handling: consistent ValueError usage
|
|
335
|
+
- [x] Test structure: Arrange→Act→Assert pattern
|
|
336
|
+
- [x] SPEC format: 5 EARS patterns used
|
|
337
|
+
- [x] Documentation: SPEC→README→docstrings
|
|
338
|
+
|
|
339
|
+
## S – Secured
|
|
340
|
+
- [x] No hardcoded secrets (use Settings.SECRET_KEY)
|
|
341
|
+
- [x] Input validation: email, password format
|
|
342
|
+
- [x] Error handling: Generic "Invalid credentials"
|
|
343
|
+
- [x] Brute-force protection: 3 attempts → lockout
|
|
344
|
+
- [x] Password hashing: bcrypt with 12 rounds
|
|
345
|
+
|
|
346
|
+
## E – Evaluated (≥85% Coverage)
|
|
347
|
+
- [x] Test coverage: 92%
|
|
348
|
+
- signup(): 95% (valid, invalid email, weak password)
|
|
349
|
+
- login(): 89% (success, invalid, locked, failed attempts)
|
|
350
|
+
- helper functions: 100%
|
|
351
|
+
- [x] Edge cases: lockout expiration, token expiration
|
|
352
|
+
- [x] Integration tests: signup→verify→login
|
|
353
|
+
|
|
354
|
+
✅ All TRUST 5 principles met!
|
|
355
|
+
```
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Alfred Development Guide - Reference
|
|
2
|
+
|
|
3
|
+
## SPEC-First TDD Workflow Details
|
|
4
|
+
|
|
5
|
+
### Phase 1: SPEC (`/alfred:1-plan`)
|
|
6
|
+
|
|
7
|
+
**Duration**: 30-60 minutes per feature
|
|
8
|
+
**Output**: `.moai/specs/SPEC-{ID}/spec.md`
|
|
9
|
+
|
|
10
|
+
#### EARS Requirement Patterns
|
|
11
|
+
|
|
12
|
+
```markdown
|
|
13
|
+
### Ubiquitous Requirements (Baseline)
|
|
14
|
+
- The system shall provide user authentication via email.
|
|
15
|
+
- The system shall validate email format before storage.
|
|
16
|
+
|
|
17
|
+
### Event-driven Requirements
|
|
18
|
+
- WHEN a user clicks 'Sign Up', the system shall display the signup form.
|
|
19
|
+
- WHEN signup is complete, the system shall send verification email.
|
|
20
|
+
- WHEN verification email expires, the system shall invalidate token.
|
|
21
|
+
|
|
22
|
+
### State-driven Requirements
|
|
23
|
+
- WHILE the user is unauthenticated, the system shall deny access to protected resources.
|
|
24
|
+
- WHILE the session is active, the system shall allow request processing.
|
|
25
|
+
|
|
26
|
+
### Optional Features
|
|
27
|
+
- WHERE the user enables 2FA, the system may require additional verification.
|
|
28
|
+
- WHERE API quota remains, the system may allow batch operations.
|
|
29
|
+
|
|
30
|
+
### Constraints
|
|
31
|
+
- IF password is invalid 3 times, the system shall lock the account.
|
|
32
|
+
- IF rate limit exceeded, the system shall return 429 error.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### SPEC Metadata Required
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
id: AUTH-001
|
|
39
|
+
title: "User Authentication System"
|
|
40
|
+
version: 0.1.0
|
|
41
|
+
status: active
|
|
42
|
+
created: 2025-11-03
|
|
43
|
+
updated: 2025-11-03
|
|
44
|
+
author: @USERNAME
|
|
45
|
+
priority: high
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Phase 2: TDD Implementation (`/alfred:2-run`)
|
|
49
|
+
|
|
50
|
+
**Duration**: 2-4 hours per SPEC
|
|
51
|
+
|
|
52
|
+
#### RED Phase (Failing Tests)
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
# tests/test_auth.py
|
|
56
|
+
import pytest
|
|
57
|
+
from src.auth import authenticate # @TEST:AUTH-001
|
|
58
|
+
|
|
59
|
+
def test_invalid_password_denies_access(): # @TEST:AUTH-001
|
|
60
|
+
"""SPEC: IF password invalid 3 times, lock account"""
|
|
61
|
+
user = create_test_user()
|
|
62
|
+
|
|
63
|
+
with pytest.raises(AuthenticationError): # RED: Will fail
|
|
64
|
+
for _ in range(3):
|
|
65
|
+
authenticate(user.email, "wrong_password")
|
|
66
|
+
|
|
67
|
+
assert user.account_locked # Then passes (GREEN)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### GREEN Phase (Minimal Implementation)
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# src/auth.py
|
|
74
|
+
from src.models import User # @CODE:AUTH-001
|
|
75
|
+
|
|
76
|
+
def authenticate(email: str, password: str) -> User: # @CODE:AUTH-001
|
|
77
|
+
"""Authenticate user with email and password."""
|
|
78
|
+
user = User.find_by_email(email)
|
|
79
|
+
|
|
80
|
+
if not user.verify_password(password):
|
|
81
|
+
user.failed_attempts += 1
|
|
82
|
+
if user.failed_attempts >= 3:
|
|
83
|
+
user.account_locked = True
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
user.failed_attempts = 0
|
|
87
|
+
return user
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### REFACTOR Phase (Improve Code)
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# Refactored: Better error handling and type hints
|
|
94
|
+
from typing import Optional # @CODE:AUTH-001
|
|
95
|
+
from src.models import User
|
|
96
|
+
from src.exceptions import AuthenticationError
|
|
97
|
+
|
|
98
|
+
MAX_FAILED_ATTEMPTS = 3 # @CODE:AUTH-001
|
|
99
|
+
|
|
100
|
+
def authenticate(email: str, password: str) -> Optional[User]: # @CODE:AUTH-001
|
|
101
|
+
"""Authenticate user and manage attempt tracking."""
|
|
102
|
+
user = User.find_by_email(email)
|
|
103
|
+
if not user:
|
|
104
|
+
raise AuthenticationError("User not found")
|
|
105
|
+
|
|
106
|
+
if not user.verify_password(password):
|
|
107
|
+
user.increment_failed_attempts(MAX_FAILED_ATTEMPTS)
|
|
108
|
+
raise AuthenticationError("Invalid credentials")
|
|
109
|
+
|
|
110
|
+
user.reset_failed_attempts()
|
|
111
|
+
return user
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Phase 3: Documentation Sync (`/alfred:3-sync`)
|
|
115
|
+
|
|
116
|
+
**Duration**: 30-60 minutes per SPEC
|
|
117
|
+
**Actions**:
|
|
118
|
+
1. Verify all @TAG chains (SPEC→TEST→CODE→DOC)
|
|
119
|
+
2. Update README, CHANGELOG
|
|
120
|
+
3. Generate sync report
|
|
121
|
+
4. Create PR to develop
|
|
122
|
+
|
|
123
|
+
## Context Engineering: JIT Loading
|
|
124
|
+
|
|
125
|
+
### When to Load What
|
|
126
|
+
|
|
127
|
+
**`/alfred:1-plan` command**:
|
|
128
|
+
```
|
|
129
|
+
Always load:
|
|
130
|
+
- .moai/project/product.md (project overview)
|
|
131
|
+
|
|
132
|
+
Optional load:
|
|
133
|
+
- .moai/project/structure.md (codebase layout)
|
|
134
|
+
- .moai/project/tech.md (technology choices)
|
|
135
|
+
|
|
136
|
+
Never load:
|
|
137
|
+
- Individual SPEC files (load only candidates)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**`/alfred:2-run` command**:
|
|
141
|
+
```
|
|
142
|
+
Always load:
|
|
143
|
+
- .moai/specs/SPEC-{ID}/spec.md (target SPEC)
|
|
144
|
+
|
|
145
|
+
Optionally load:
|
|
146
|
+
- development-guide.md (if testing TDD patterns)
|
|
147
|
+
- SPEC-related files (if chained features)
|
|
148
|
+
|
|
149
|
+
Never load:
|
|
150
|
+
- Unrelated SPECs, docs, analysis files
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**`/alfred:3-sync` command**:
|
|
154
|
+
```
|
|
155
|
+
Always load:
|
|
156
|
+
- .moai/reports/sync-report.md (previous sync state)
|
|
157
|
+
|
|
158
|
+
Optional load:
|
|
159
|
+
- Changed SPEC files (only ones modified)
|
|
160
|
+
- TAG validation results
|
|
161
|
+
|
|
162
|
+
Never load:
|
|
163
|
+
- Old sync reports (keep recent only)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## TRUST 5 Principles Checklist
|
|
167
|
+
|
|
168
|
+
### T – Test-Driven (SPEC-Aligned)
|
|
169
|
+
- [ ] SPEC written with EARS format
|
|
170
|
+
- [ ] RED phase: failing tests written first
|
|
171
|
+
- [ ] GREEN phase: code passes all tests
|
|
172
|
+
- [ ] REFACTOR: improved code still passes
|
|
173
|
+
- [ ] @TAG chain: SPEC→TEST→CODE→DOC
|
|
174
|
+
|
|
175
|
+
### R – Readable
|
|
176
|
+
- [ ] Variable names are descriptive
|
|
177
|
+
- [ ] Functions do one thing
|
|
178
|
+
- [ ] Comments explain WHY, not WHAT
|
|
179
|
+
- [ ] Code follows language style guide
|
|
180
|
+
- [ ] Documentation is current
|
|
181
|
+
|
|
182
|
+
### U – Unified
|
|
183
|
+
- [ ] Consistent naming conventions
|
|
184
|
+
- [ ] Same patterns across codebase
|
|
185
|
+
- [ ] Shared language (EARS for specs)
|
|
186
|
+
- [ ] Common test structure
|
|
187
|
+
- [ ] Standardized documentation
|
|
188
|
+
|
|
189
|
+
### S – Secured
|
|
190
|
+
- [ ] No hardcoded secrets
|
|
191
|
+
- [ ] Input validation present
|
|
192
|
+
- [ ] Error handling included
|
|
193
|
+
- [ ] OWASP top 10 considered
|
|
194
|
+
- [ ] Security review completed
|
|
195
|
+
|
|
196
|
+
### E – Evaluated (85%+ Coverage)
|
|
197
|
+
- [ ] Test coverage ≥85%
|
|
198
|
+
- [ ] All functions tested
|
|
199
|
+
- [ ] Error paths covered
|
|
200
|
+
- [ ] Edge cases included
|
|
201
|
+
- [ ] Integration tests exist
|
|
202
|
+
|
|
203
|
+
## @TAG Chain Validation
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Check all TAGs
|
|
207
|
+
rg '@(SPEC|TEST|CODE|DOC):' -n .moai/specs/ tests/ src/ docs/
|
|
208
|
+
|
|
209
|
+
# Find orphan SPECs (no @CODE references)
|
|
210
|
+
rg '@SPEC:AUTH-001' -n .moai/specs/
|
|
211
|
+
rg '@CODE:AUTH-001' -n src/ # Should have at least one
|
|
212
|
+
|
|
213
|
+
# Find orphan CODE tags (SPEC missing)
|
|
214
|
+
rg '@CODE:AUTH-001' -n src/
|
|
215
|
+
rg '@SPEC:AUTH-001' -n .moai/specs/ # Should exist
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Common Commands
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Start new SPEC
|
|
222
|
+
mkdir -p .moai/specs/SPEC-AUTH-001
|
|
223
|
+
|
|
224
|
+
# Check for duplicates
|
|
225
|
+
rg "@SPEC:AUTH-001" .moai/specs/ # Should return 0-1 hits
|
|
226
|
+
|
|
227
|
+
# List all SPECs by status
|
|
228
|
+
rg "^status:" .moai/specs/SPEC-*/spec.md
|
|
229
|
+
|
|
230
|
+
# Validate all YAML frontmatter
|
|
231
|
+
for f in .moai/specs/SPEC-*/spec.md; do
|
|
232
|
+
echo "Checking $f..."
|
|
233
|
+
head -20 "$f" | grep -E "^(id|version|status|created|updated|author|priority):"
|
|
234
|
+
done
|
|
235
|
+
|
|
236
|
+
# Generate SPEC index
|
|
237
|
+
echo "# SPEC Inventory" && \
|
|
238
|
+
rg "^id:" .moai/specs/SPEC-*/spec.md | sed 's/:id:/: /'
|
|
239
|
+
```
|