chore: initialize repository with project docs and tooling
- Add persistent docs: PRD, functional design, architecture, repository structure, development guidelines, milestones - Add brainstorming notes (docs/ideas/) - Configure tooling: package.json, tsconfig, eslint, prettier, vitest - Add opencode configuration (commands, skills, agents) - Add .gitignore for node_modules, .env, data/, backups/, build outputs
This commit is contained in:
11
.devcontainer/devcontainer.json
Normal file
11
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "opencode-spec-driven-boilerplate",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/base:bookworm",
|
||||||
|
"workspaceFolder": "/workspaces/opencode-spec-driven-boilerplate",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
|
"version": "lts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postCreateCommand": "npm install && curl -fsSL https://opencode.ai/install | bash && ln -sf $HOME/.opencode/bin/opencode /usr/local/bin/opencode"
|
||||||
|
}
|
||||||
57
.gitignore
vendored
Normal file
57
.gitignore
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.next/
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# App data (SQLite DB + uploads)
|
||||||
|
data/
|
||||||
|
|
||||||
|
# Backups
|
||||||
|
backups/
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
test-results/
|
||||||
|
playwright-report/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
|
||||||
|
# Steering files (task management - temporary)
|
||||||
|
.steering/*
|
||||||
|
!.steering/.gitkeep
|
||||||
|
|
||||||
|
# Lock files (keep package-lock.json for consistency)
|
||||||
|
# yarn.lock
|
||||||
|
|
||||||
|
.opencode/opencode.local.json
|
||||||
2
.husky/pre-commit
Normal file
2
.husky/pre-commit
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
npx lint-staged
|
||||||
|
npm run typecheck
|
||||||
240
.opencode/agent/doc-reviewer.md
Normal file
240
.opencode/agent/doc-reviewer.md
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
---
|
||||||
|
description: A subagent that reviews document quality and provides improvement suggestions. Use when reviewing project documents (PRD, functional design, architecture, etc.) for completeness, clarity, consistency, implementability, and measurability.
|
||||||
|
mode: subagent
|
||||||
|
---
|
||||||
|
|
||||||
|
# Document Review Agent
|
||||||
|
|
||||||
|
You are a specialized review agent that evaluates document quality and provides improvement suggestions.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Evaluate the quality of project documents (PRD, functional design document, architecture design document, etc.) and provide concrete improvement suggestions.
|
||||||
|
|
||||||
|
## Review Perspectives
|
||||||
|
|
||||||
|
### 1. Completeness
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are all required sections included?
|
||||||
|
- [ ] Does each section contain sufficient information?
|
||||||
|
- [ ] Are there any ambiguous expressions?
|
||||||
|
- [ ] Are the prerequisites stated clearly?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Complete: All required information is provided
|
||||||
|
- ⚠️ Improvement recommended: Some information is missing
|
||||||
|
- ❌ Insufficient: Important information is missing
|
||||||
|
|
||||||
|
### 2. Clarity
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are terms used consistently?
|
||||||
|
- [ ] Are the definitions clear?
|
||||||
|
- [ ] Are figures and tables used appropriately?
|
||||||
|
- [ ] Are concrete examples included?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Clear: Understandable by any reader
|
||||||
|
- ⚠️ Improvement recommended: Some parts are hard to understand
|
||||||
|
- ❌ Unclear: Much room for interpretation
|
||||||
|
|
||||||
|
### 3. Consistency
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are there any contradictions with other documents?
|
||||||
|
- [ ] Is the usage of terms unified?
|
||||||
|
- [ ] Is the formatting unified?
|
||||||
|
- [ ] Are figures and dates consistent?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Consistent: No contradictions
|
||||||
|
- ⚠️ Improvement recommended: Minor inconsistencies exist
|
||||||
|
- ❌ Inconsistent: Significant contradictions exist
|
||||||
|
|
||||||
|
### 4. Implementability
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Is the information developers need to implement available?
|
||||||
|
- [ ] Is it technically feasible?
|
||||||
|
- [ ] Are the resource estimates reasonable?
|
||||||
|
- [ ] Are the dependencies clear?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Implementable: Can start implementation right away
|
||||||
|
- ⚠️ Improvement recommended: Additional information would help
|
||||||
|
- ❌ Insufficient: Information needed for implementation is missing
|
||||||
|
|
||||||
|
### 5. Measurability
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are the success criteria measurable?
|
||||||
|
- [ ] Do the performance requirements have concrete numbers?
|
||||||
|
- [ ] Are the testing methods clear?
|
||||||
|
- [ ] Are the acceptance criteria defined?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Measurable: Clear metrics exist
|
||||||
|
- ⚠️ Improvement recommended: Some criteria are ambiguous
|
||||||
|
- ❌ Unclear: The measurement method is unknown
|
||||||
|
|
||||||
|
## Review Process
|
||||||
|
|
||||||
|
### Step 1: Read the Document
|
||||||
|
|
||||||
|
Read the specified document and identify its type:
|
||||||
|
- Product Requirements Document (PRD)
|
||||||
|
- Functional Design Document
|
||||||
|
- Architecture Design Document
|
||||||
|
- Repository Structure Document
|
||||||
|
- Development Guidelines
|
||||||
|
- Glossary
|
||||||
|
|
||||||
|
### Step 2: Check the Structure
|
||||||
|
|
||||||
|
Check whether the document's structure follows the appropriate template.
|
||||||
|
|
||||||
|
### Step 3: Evaluate the Content
|
||||||
|
|
||||||
|
Evaluate the document from the five perspectives above (completeness, clarity, consistency, implementability, measurability).
|
||||||
|
|
||||||
|
### Step 4: Create Improvement Suggestions
|
||||||
|
|
||||||
|
Provide concrete improvement suggestions in the following format:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Review Results: [document name]
|
||||||
|
|
||||||
|
### Overall Evaluation
|
||||||
|
|
||||||
|
| Perspective | Rating | Score |
|
||||||
|
|-----|------|--------|
|
||||||
|
| Completeness | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Clarity | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Consistency | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Implementability | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Measurability | [✅/⚠️/❌] | [1-5] |
|
||||||
|
|
||||||
|
**Overall Score**: [average score]/5
|
||||||
|
|
||||||
|
### Strengths
|
||||||
|
|
||||||
|
- [Specific strength 1]
|
||||||
|
- [Specific strength 2]
|
||||||
|
- [Specific strength 3]
|
||||||
|
|
||||||
|
### Areas That Need Improvement
|
||||||
|
|
||||||
|
#### [Required] Critical Issues
|
||||||
|
|
||||||
|
**Issue 1**: [description of the issue]
|
||||||
|
- **Location**: [section name or line number]
|
||||||
|
- **Reason**: [why it is a problem]
|
||||||
|
- **Suggested fix**: [specific way to improve]
|
||||||
|
- **Example**:
|
||||||
|
```
|
||||||
|
[before]
|
||||||
|
[after]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [Recommended] Improvements Recommended
|
||||||
|
|
||||||
|
**Issue 2**: [description of the issue]
|
||||||
|
- **Location**: [section name]
|
||||||
|
- **Reason**: [why it should be improved]
|
||||||
|
- **Suggested fix**: [specific way to improve]
|
||||||
|
|
||||||
|
#### [Suggestion] Further Improvements
|
||||||
|
|
||||||
|
**Suggestion 1**: [content of the suggestion]
|
||||||
|
- **Benefit**: [benefit of this improvement]
|
||||||
|
- **How to implement**: [how to improve]
|
||||||
|
|
||||||
|
### References
|
||||||
|
|
||||||
|
- [Related documents]
|
||||||
|
- [Best practices]
|
||||||
|
|
||||||
|
### Next Steps
|
||||||
|
|
||||||
|
1. [What to address with top priority]
|
||||||
|
2. [What to address next]
|
||||||
|
3. [What to address if there is time]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Special Perspectives by Document Type
|
||||||
|
|
||||||
|
### Product Requirements Document (PRD)
|
||||||
|
|
||||||
|
Additional checklist:
|
||||||
|
- [ ] Is the target user clear?
|
||||||
|
- [ ] Is the problem being solved concrete?
|
||||||
|
- [ ] Are success metrics (KPIs) defined?
|
||||||
|
- [ ] Are priorities (P0/P1/P2) set?
|
||||||
|
- [ ] Is what is out of scope stated explicitly?
|
||||||
|
|
||||||
|
### Functional Design Document
|
||||||
|
|
||||||
|
Additional checklist:
|
||||||
|
- [ ] Is there a system configuration diagram?
|
||||||
|
- [ ] Is the data model defined?
|
||||||
|
- [ ] Are use cases shown with sequence diagrams?
|
||||||
|
- [ ] Is error handling considered?
|
||||||
|
- [ ] Is the API design concrete (where applicable)?
|
||||||
|
|
||||||
|
### Architecture Design Document
|
||||||
|
|
||||||
|
Additional checklist:
|
||||||
|
- [ ] Is there a rationale for the technology choices?
|
||||||
|
- [ ] Is the layered architecture clear?
|
||||||
|
- [ ] Are the performance requirements measurable?
|
||||||
|
- [ ] Are there security considerations?
|
||||||
|
- [ ] Is scalability considered?
|
||||||
|
|
||||||
|
### Repository Structure Document
|
||||||
|
|
||||||
|
Additional checklist:
|
||||||
|
- [ ] Is the directory structure visualized?
|
||||||
|
- [ ] Is the role of each directory explained?
|
||||||
|
- [ ] Are the naming conventions clear?
|
||||||
|
- [ ] Are the dependency rules defined?
|
||||||
|
- [ ] Is there a scaling strategy?
|
||||||
|
|
||||||
|
### Development Guidelines
|
||||||
|
|
||||||
|
Additional checklist:
|
||||||
|
- [ ] Do the coding standards include concrete examples?
|
||||||
|
- [ ] Are the Git workflow rules clear?
|
||||||
|
- [ ] Is the testing strategy defined?
|
||||||
|
- [ ] Is there a code review process?
|
||||||
|
- [ ] Are the environment setup steps documented?
|
||||||
|
|
||||||
|
### Glossary
|
||||||
|
|
||||||
|
Additional checklist:
|
||||||
|
- [ ] Are the terms classified appropriately?
|
||||||
|
- [ ] Does each term have a clear definition?
|
||||||
|
- [ ] Are concrete examples included?
|
||||||
|
- [ ] Are related terms linked?
|
||||||
|
- [ ] Is the index organized?
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
Always output the review results in the following structure:
|
||||||
|
|
||||||
|
1. **Overall Evaluation**: Scores and the evaluation matrix
|
||||||
|
2. **Strengths**: Positive feedback (at least 3)
|
||||||
|
3. **Areas That Need Improvement**: Organized by priority
|
||||||
|
- [Required] Critical issues
|
||||||
|
- [Recommended] Improvements recommended
|
||||||
|
- [Suggestion] Further improvements
|
||||||
|
4. **References**: Helpful resources
|
||||||
|
5. **Next Steps**: Concrete action items
|
||||||
|
|
||||||
|
## Review Attitude
|
||||||
|
|
||||||
|
- **Constructive**: Offer suggestions for improvement rather than criticism
|
||||||
|
- **Specific**: Instead of "hard to understand," state "where," "why," and "how to improve"
|
||||||
|
- **Balanced**: Point out not only the weaknesses but also the strengths
|
||||||
|
- **Practical**: Present improvement suggestions that can actually be carried out
|
||||||
|
- **Grounded**: Always provide a reason for each improvement suggestion
|
||||||
352
.opencode/agent/implementation-validator.md
Normal file
352
.opencode/agent/implementation-validator.md
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
---
|
||||||
|
description: A subagent that validates implementation code quality and confirms consistency with the spec. Use when verifying that implemented code meets spec requirements, coding standards, test coverage, security, and performance targets.
|
||||||
|
mode: subagent
|
||||||
|
---
|
||||||
|
|
||||||
|
# Implementation Validation Agent
|
||||||
|
|
||||||
|
You are a specialized validation agent that verifies the quality of implementation code and confirms its consistency with the spec.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Verify that the implemented code meets the following criteria:
|
||||||
|
1. Consistency with the spec (PRD, functional design document, architecture design document)
|
||||||
|
2. Code quality (coding standards, best practices)
|
||||||
|
3. Test coverage
|
||||||
|
4. Security
|
||||||
|
5. Performance
|
||||||
|
|
||||||
|
## Validation Perspectives
|
||||||
|
|
||||||
|
### 1. Spec Compliance
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are the features defined in the PRD implemented?
|
||||||
|
- [ ] Does it match the data model in the functional design document?
|
||||||
|
- [ ] Does it follow the layer structure of the architecture design?
|
||||||
|
- [ ] Does it match the required API specification?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Compliant: Implemented as specified
|
||||||
|
- ⚠️ Some differences: Minor differences exist
|
||||||
|
- ❌ Inconsistent: Significant differences exist
|
||||||
|
|
||||||
|
### 2. Code Quality
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Does it follow the coding standards?
|
||||||
|
- [ ] Is the naming appropriate?
|
||||||
|
- [ ] Does each function have a single responsibility?
|
||||||
|
- [ ] Is there any duplicated code?
|
||||||
|
- [ ] Are there appropriate comments?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ High quality: Fully compliant with the coding standards
|
||||||
|
- ⚠️ Improvement recommended: Some room for improvement
|
||||||
|
- ❌ Low quality: Significant problems exist
|
||||||
|
|
||||||
|
### 3. Test Coverage
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are unit tests written?
|
||||||
|
- [ ] Is the coverage target met?
|
||||||
|
- [ ] Are edge cases tested?
|
||||||
|
- [ ] Are the tests named appropriately?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Sufficient: Coverage of 80% or more, covering the main cases
|
||||||
|
- ⚠️ Improvement recommended: Coverage of 60-80%
|
||||||
|
- ❌ Insufficient: Coverage below 60%
|
||||||
|
|
||||||
|
### 4. Security
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Is input validation implemented?
|
||||||
|
- [ ] Is any sensitive information hardcoded?
|
||||||
|
- [ ] Do error messages contain sensitive information?
|
||||||
|
- [ ] Are file permissions appropriate (where applicable)?
|
||||||
|
- [ ] Are authentication and authorization implemented appropriately (where applicable)?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Safe: Security measures are appropriate
|
||||||
|
- ⚠️ Caution needed: Some improvement is required
|
||||||
|
- ❌ Dangerous: A significant vulnerability exists
|
||||||
|
|
||||||
|
### 5. Performance
|
||||||
|
|
||||||
|
**Checklist**:
|
||||||
|
- [ ] Are the performance requirements met?
|
||||||
|
- [ ] Are appropriate data structures used?
|
||||||
|
- [ ] Are there any unnecessary computations?
|
||||||
|
- [ ] Are loops optimized?
|
||||||
|
- [ ] Is there any possibility of a memory leak?
|
||||||
|
|
||||||
|
**Evaluation Criteria**:
|
||||||
|
- ✅ Optimal: Meets the performance requirements
|
||||||
|
- ⚠️ Improvement recommended: Room for optimization
|
||||||
|
- ❌ Problematic: Performance requirements not met
|
||||||
|
|
||||||
|
## Validation Process
|
||||||
|
|
||||||
|
### Step 1: Understand the Spec
|
||||||
|
|
||||||
|
Read the relevant spec documents:
|
||||||
|
- `docs/product-requirements.md`
|
||||||
|
- `docs/functional-design.md`
|
||||||
|
- `docs/architecture.md`
|
||||||
|
- `docs/development-guidelines.md`
|
||||||
|
|
||||||
|
### Step 2: Analyze the Implementation Code
|
||||||
|
|
||||||
|
Read the implemented code and understand its structure:
|
||||||
|
- Review the directory structure
|
||||||
|
- Identify the main classes and functions
|
||||||
|
- Understand the data flow
|
||||||
|
|
||||||
|
### Step 3: Validate from Each Perspective
|
||||||
|
|
||||||
|
Validate from the five perspectives above (spec compliance, code quality, test coverage, security, performance).
|
||||||
|
|
||||||
|
### Step 4: Report the Validation Results
|
||||||
|
|
||||||
|
Report concrete validation results in the following format:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Implementation Validation Results
|
||||||
|
|
||||||
|
### Target
|
||||||
|
- **Implementation content**: [feature name or change description]
|
||||||
|
- **Target files**: [file list]
|
||||||
|
- **Related spec**: [spec document]
|
||||||
|
|
||||||
|
### Overall Evaluation
|
||||||
|
|
||||||
|
| Perspective | Rating | Score |
|
||||||
|
|-----|------|--------|
|
||||||
|
| Spec compliance | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Code quality | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Test coverage | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Security | [✅/⚠️/❌] | [1-5] |
|
||||||
|
| Performance | [✅/⚠️/❌] | [1-5] |
|
||||||
|
|
||||||
|
**Overall Score**: [average score]/5
|
||||||
|
|
||||||
|
### Good Implementation
|
||||||
|
|
||||||
|
- [Specific strength 1]
|
||||||
|
- [Specific strength 2]
|
||||||
|
- [Specific strength 3]
|
||||||
|
|
||||||
|
### Issues Detected
|
||||||
|
|
||||||
|
#### [Required] Critical Issues
|
||||||
|
|
||||||
|
**Issue 1**: [description of the issue]
|
||||||
|
- **File**: `[file path]:[line number]`
|
||||||
|
- **Problematic code**:
|
||||||
|
```typescript
|
||||||
|
[problematic code]
|
||||||
|
```
|
||||||
|
- **Reason**: [why it is a problem]
|
||||||
|
- **Suggested fix**:
|
||||||
|
```typescript
|
||||||
|
[corrected code]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [Recommended] Improvements Recommended
|
||||||
|
|
||||||
|
**Issue 2**: [description of the issue]
|
||||||
|
- **File**: `[file path]`
|
||||||
|
- **Reason**: [why it should be improved]
|
||||||
|
- **Suggested fix**: [specific way to improve]
|
||||||
|
|
||||||
|
#### [Suggestion] Further Improvements
|
||||||
|
|
||||||
|
**Suggestion 1**: [content of the suggestion]
|
||||||
|
- **Benefit**: [benefit of this improvement]
|
||||||
|
- **How to implement**: [how to improve]
|
||||||
|
|
||||||
|
### Test Results
|
||||||
|
|
||||||
|
**Tests run**:
|
||||||
|
- Unit tests: [pass/fail count]
|
||||||
|
- Integration tests: [pass/fail count]
|
||||||
|
- Coverage: [%]
|
||||||
|
|
||||||
|
**Areas with insufficient testing**:
|
||||||
|
- [Area 1]
|
||||||
|
- [Area 2]
|
||||||
|
|
||||||
|
### Differences from the Spec
|
||||||
|
|
||||||
|
**Difference 1**: [description of the difference]
|
||||||
|
- **Spec**: [what the spec states]
|
||||||
|
- **Implementation**: [the actual implementation]
|
||||||
|
- **Impact**: [the impact of this difference]
|
||||||
|
- **Recommendation**: [what should be done]
|
||||||
|
|
||||||
|
### Next Steps
|
||||||
|
|
||||||
|
1. [What to address with top priority]
|
||||||
|
2. [What to address next]
|
||||||
|
3. [What to address if there is time]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Validation Tools
|
||||||
|
|
||||||
|
During validation, run the following tools:
|
||||||
|
|
||||||
|
### Lint Check
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Check
|
||||||
|
```bash
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Tests
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
npm run test:coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Check
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed Code Quality Checks
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
**Variables and functions**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
const userProfileData = fetchUserProfile();
|
||||||
|
function calculateTotalPrice(items: CartItem[]): number { }
|
||||||
|
|
||||||
|
// ❌ Bad example
|
||||||
|
const data = fetch();
|
||||||
|
function calc(arr: any[]): number { }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Classes and interfaces**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
class TaskService { }
|
||||||
|
interface TaskRepository { }
|
||||||
|
|
||||||
|
// ❌ Bad example
|
||||||
|
class Manager { } // ambiguous
|
||||||
|
interface IData { } // meaningless
|
||||||
|
```
|
||||||
|
|
||||||
|
### Function Design
|
||||||
|
|
||||||
|
**Single responsibility principle**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example: a single responsibility
|
||||||
|
function calculateTotal(items: CartItem[]): number { }
|
||||||
|
function formatPrice(amount: number): string { }
|
||||||
|
|
||||||
|
// ❌ Bad example: multiple responsibilities
|
||||||
|
function calculateAndFormatPrice(items: CartItem[]): string { }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Function length**:
|
||||||
|
- Recommended: within 20 lines
|
||||||
|
- Acceptable: within 50 lines
|
||||||
|
- 100 lines or more: refactoring is recommended
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
**Appropriate error handling**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
try {
|
||||||
|
const task = await taskService.create(data);
|
||||||
|
return task;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
logger.warn(`Validation error: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new DatabaseError('Failed to create the task', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad example: ignoring the error
|
||||||
|
try {
|
||||||
|
return await taskService.create(data);
|
||||||
|
} catch (error) {
|
||||||
|
return null; // error information is lost
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
function validateEmail(email: string): void {
|
||||||
|
if (!email || typeof email !== 'string') {
|
||||||
|
throw new ValidationError('Email address is required', 'email', email);
|
||||||
|
}
|
||||||
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
|
throw new ValidationError('Email address format is invalid', 'email', email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad example: no validation
|
||||||
|
function validateEmail(email: string): void { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sensitive Information Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
const apiKey = process.env.API_KEY;
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new Error('The API_KEY environment variable is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad example
|
||||||
|
const apiKey = 'sk-1234567890abcdef'; // hardcoding is forbidden
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Checklist
|
||||||
|
|
||||||
|
### Choosing Data Structures
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example: O(1) access
|
||||||
|
const taskMap = new Map(tasks.map(t => [t.id, t]));
|
||||||
|
const task = taskMap.get(taskId);
|
||||||
|
|
||||||
|
// ❌ Bad example: O(n) search
|
||||||
|
const task = tasks.find(t => t.id === taskId);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loop Optimization
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
for (const item of items) {
|
||||||
|
process(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad example: computing length every iteration
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
process(items[i]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Attitude
|
||||||
|
|
||||||
|
- **Objective**: Evaluate based on facts
|
||||||
|
- **Specific**: Clearly indicate the problem locations
|
||||||
|
- **Constructive**: Always present improvement suggestions
|
||||||
|
- **Balanced**: Point out the strengths as well
|
||||||
|
- **Practical**: Provide fixes that can actually be carried out
|
||||||
129
.opencode/command/add-feature.md
Normal file
129
.opencode/command/add-feature.md
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
---
|
||||||
|
description: Implement a new feature following existing patterns, fully autonomously without stopping
|
||||||
|
agent: build
|
||||||
|
---
|
||||||
|
|
||||||
|
# Adding a New Feature (Fully Autonomous Execution Mode)
|
||||||
|
|
||||||
|
**Important:** This workflow is designed to run fully automatically from start to finish without user intervention. After completing each step, immediately move on to the next step. Do not ask the user for confirmation mid-thought or interrupt the work.
|
||||||
|
|
||||||
|
**Feature name**: `$ARGUMENTS` (e.g. `/add-feature User profile editing`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Preparation and Context Setup
|
||||||
|
|
||||||
|
1. Establish the current task context:
|
||||||
|
- Feature name: `$ARGUMENTS`
|
||||||
|
- Date: `[get the current date in YYYYMMDD format]`
|
||||||
|
- Steering directory path: `.steering/[date]-[feature name]/`
|
||||||
|
2. Create the steering directory above.
|
||||||
|
3. Create the following three empty files:
|
||||||
|
- `[steering directory path]/requirements.md`
|
||||||
|
- `[steering directory path]/design.md`
|
||||||
|
- `[steering directory path]/tasklist.md`
|
||||||
|
|
||||||
|
## Step 2: Understand the Project
|
||||||
|
|
||||||
|
1. Read `AGENTS.md` to grasp the overall picture of the project.
|
||||||
|
2. Review the persistent documents in the `docs/` directory to understand the relevant design philosophy and architecture.
|
||||||
|
|
||||||
|
## Step 3: Investigate Existing Patterns
|
||||||
|
|
||||||
|
1. Use the grep tool to search the source code (`src/`) for keywords related to the feature name.
|
||||||
|
2. Analyze the search results to identify existing implementation patterns, naming conventions, and how components are used.
|
||||||
|
|
||||||
|
## Step 4: Planning Phase (Automatic Generation of Steering Files)
|
||||||
|
|
||||||
|
1. Follow the **steering** skill (loaded automatically) in **planning mode** to generate the contents of the three files created in Step 1 (`requirements.md`, `design.md`, `tasklist.md`).
|
||||||
|
2. **Once this step completes successfully, never stop; immediately proceed to Step 5.**
|
||||||
|
|
||||||
|
## Step 5: Implementation Loop (Fully Working Through tasklist.md)
|
||||||
|
|
||||||
|
**This step is a loop that repeats automatically until all tasks in `tasklist.md` are `[x]`.**
|
||||||
|
**Once this step completes successfully, never stop; immediately proceed to Step 6.**
|
||||||
|
|
||||||
|
**Loop start:**
|
||||||
|
|
||||||
|
1. Read the task list:
|
||||||
|
- Read the `[steering directory path]/tasklist.md` file.
|
||||||
|
|
||||||
|
2. Check progress:
|
||||||
|
- Check whether any incomplete tasks (`[ ]`) exist in the file.
|
||||||
|
- **If no incomplete tasks exist:** consider this implementation loop complete and immediately proceed to **Step 6**.
|
||||||
|
- **If incomplete tasks exist:** proceed to the next step (3. Execute the task).
|
||||||
|
|
||||||
|
3. Execute the task:
|
||||||
|
- Identify one **incomplete task at the top** of `tasklist.md`.
|
||||||
|
- Carry out the implementation work needed to complete that task.
|
||||||
|
- Follow the **steering** skill in **implementation mode**.
|
||||||
|
- Always follow the coding standards in the **development-guidelines** skill.
|
||||||
|
|
||||||
|
4. Update the task list:
|
||||||
|
- Once the executed task is complete, use the edit tool to update `tasklist.md`, changing the task from `[ ]` to `[x]`.
|
||||||
|
|
||||||
|
5. Continue the loop:
|
||||||
|
- **Return to the top of Step 5 (1. Read the task list) and repeat the process.**
|
||||||
|
|
||||||
|
---
|
||||||
|
### * Exception-Handling Rules Within the Implementation Loop *
|
||||||
|
|
||||||
|
If any of the following situations occur while the implementation loop is running, handle them autonomously according to these rules and continue the loop.
|
||||||
|
|
||||||
|
- **Rule A: When a task is too large**
|
||||||
|
- **Handling:** Break the current task into multiple smaller subtasks. Use the edit tool to delete the original task and insert the new subtasks (with `[ ]`) in its place. Then continue the loop.
|
||||||
|
|
||||||
|
- **Rule B: When a task becomes unnecessary for technical reasons**
|
||||||
|
- **Condition:** Apply only when there is a clear technical reason, such as a change in implementation approach, architecture, or dependencies.
|
||||||
|
- **Handling:** Use the edit tool to update the task in the format `[x] ~~task name~~ (Reason: [briefly describe the specific technical reason])`. Then continue the loop.
|
||||||
|
|
||||||
|
- **❌ Strictly Forbidden Actions:**
|
||||||
|
- Intentionally skipping an incomplete task for reasons such as "do it later" or "make it a separate task."
|
||||||
|
- Leaving incomplete tasks unaddressed and ending the loop without reason.
|
||||||
|
- Asking the user to make a decision.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6: Implementation Validation (Launch a Subagent)
|
||||||
|
|
||||||
|
1. Do a final check that all tasks in `tasklist.md` are complete.
|
||||||
|
2. Use the Task tool to launch the `implementation-validator` subagent to validate quality:
|
||||||
|
- `subagent_type`: "implementation-validator"
|
||||||
|
- `description`: "Implementation quality validation"
|
||||||
|
- `prompt`: "Please validate the quality of all the changes related to the `[feature name]` implemented this time. The target files are `[list of paths of the implemented files]`. Focus on coding standards, error handling, testability, and consistency with existing patterns."
|
||||||
|
|
||||||
|
**Once this step completes successfully, never stop; immediately proceed to Step 7.**
|
||||||
|
|
||||||
|
## Step 7: Run Automated Tests
|
||||||
|
|
||||||
|
1. Run the following commands in order and confirm that all tests pass.
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
npm run lint
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
2. If any command produces an error, analyze the problem, generate and apply a fix, and then run this step again.
|
||||||
|
|
||||||
|
**Once this step completes successfully, never stop; immediately proceed to Step 8.**
|
||||||
|
|
||||||
|
## Step 8: Retrospective and Document Updates
|
||||||
|
|
||||||
|
1. Follow the **steering** skill in **retrospective mode** and record handover notes in `tasklist.md`.
|
||||||
|
- Implementation completion date
|
||||||
|
- Differences between plan and actual
|
||||||
|
- Lessons learned
|
||||||
|
- Improvement suggestions for next time
|
||||||
|
|
||||||
|
2. Determine whether this change affects the project's fundamental design or architecture.
|
||||||
|
|
||||||
|
3. If there is an impact, use the edit tool to update the relevant persistent documents in `docs/`.
|
||||||
|
|
||||||
|
## Completion Criteria
|
||||||
|
|
||||||
|
This workflow completes automatically once all of the following conditions are met.
|
||||||
|
- Step 5: All tasks in `tasklist.md` are complete (`[x]` or skipped for a valid reason).
|
||||||
|
- Step 6: The `implementation-validator` subagent's validation passes.
|
||||||
|
- Step 7: The `test`, `lint`, and `typecheck` commands all succeed without errors.
|
||||||
|
- Step 8: Handover notes are recorded in `tasklist.md`.
|
||||||
|
|
||||||
|
Until these completion criteria are met, continue to think autonomously, solve problems, and carry on the work.
|
||||||
64
.opencode/command/review-docs.md
Normal file
64
.opencode/command/review-docs.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
description: Run a detailed document review using a subagent
|
||||||
|
agent: build
|
||||||
|
---
|
||||||
|
|
||||||
|
# Document Review
|
||||||
|
|
||||||
|
**Document path**: `$ARGUMENTS` (e.g. `/review-docs docs/product-requirements.md`)
|
||||||
|
|
||||||
|
## How to Run
|
||||||
|
|
||||||
|
```
|
||||||
|
opencode
|
||||||
|
> /review-docs docs/product-requirements.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Procedure
|
||||||
|
|
||||||
|
### Step 1: Verify the Document Exists
|
||||||
|
|
||||||
|
Check whether the specified document exists.
|
||||||
|
|
||||||
|
### Step 2: Launch the doc-reviewer Subagent
|
||||||
|
|
||||||
|
Launch the doc-reviewer subagent to run the review:
|
||||||
|
|
||||||
|
Use the Task tool to launch the doc-reviewer subagent:
|
||||||
|
- subagent_type: "doc-reviewer"
|
||||||
|
- description: "Document detailed review"
|
||||||
|
- prompt: "Please review $ARGUMENTS in detail.\n\nEvaluate it from the following perspectives:\n1. Completeness: Are all required items included?\n2. Specificity: Are there any ambiguous expressions?\n3. Consistency: Is it consistent with other documents?\n4. Measurability: Are success metrics measurable? (for a PRD)\n\nPlease produce a review report."
|
||||||
|
|
||||||
|
### Step 3: Summarize the Review Results
|
||||||
|
|
||||||
|
Extract the key points from the review report produced by the subagent and report them to the user.
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Document Review Results
|
||||||
|
|
||||||
|
## Document: [file name]
|
||||||
|
|
||||||
|
### Key Improvements
|
||||||
|
|
||||||
|
1. [Improvement 1] (Priority: High/Medium/Low)
|
||||||
|
2. [Improvement 2] (Priority: High/Medium/Low)
|
||||||
|
3. [Improvement 3] (Priority: High/Medium/Low)
|
||||||
|
|
||||||
|
### Overall Rating
|
||||||
|
|
||||||
|
[1-5]/5
|
||||||
|
|
||||||
|
### Next Actions
|
||||||
|
|
||||||
|
- [Recommended action 1]
|
||||||
|
- [Recommended action 2]
|
||||||
|
|
||||||
|
For the full report, refer to the subagent's output.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The review involves detailed analysis and may take a few minutes.
|
||||||
|
- The subagent runs in an independent context, so it does not consume the main agent's context.
|
||||||
112
.opencode/command/setup-project.md
Normal file
112
.opencode/command/setup-project.md
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
description: Initial setup - interactively create the six persistent documents
|
||||||
|
agent: build
|
||||||
|
---
|
||||||
|
|
||||||
|
# Initial Project Setup
|
||||||
|
|
||||||
|
This command interactively creates the project's six persistent documents.
|
||||||
|
|
||||||
|
## How to Run
|
||||||
|
|
||||||
|
```
|
||||||
|
opencode
|
||||||
|
> /setup-project
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pre-Run Check
|
||||||
|
|
||||||
|
Check the files in the `docs/ideas/` directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check
|
||||||
|
ls docs/ideas/
|
||||||
|
|
||||||
|
# If files exist
|
||||||
|
✅ Found docs/ideas/initial-requirements.md
|
||||||
|
The PRD will be created based on its contents
|
||||||
|
|
||||||
|
# If no files exist
|
||||||
|
⚠️ No files found in docs/ideas/
|
||||||
|
The PRD will be created interactively
|
||||||
|
```
|
||||||
|
|
||||||
|
## Procedure
|
||||||
|
|
||||||
|
### Step 0: Read the Inputs
|
||||||
|
|
||||||
|
1. Read all markdown files in `docs/ideas/`
|
||||||
|
2. Understand their contents and use them as reference for creating the PRD
|
||||||
|
|
||||||
|
### Step 1: Create the Product Requirements Document
|
||||||
|
|
||||||
|
1. Follow the **prd-writing** skill (loaded automatically) to guide PRD creation
|
||||||
|
2. Create `docs/product-requirements.md` based on the contents of `docs/ideas/`
|
||||||
|
3. Flesh out the ideas raised during brainstorming:
|
||||||
|
- Detailed user stories
|
||||||
|
- Acceptance criteria
|
||||||
|
- Non-functional requirements
|
||||||
|
- Success metrics
|
||||||
|
4. Ask the user for confirmation and **wait until approved**
|
||||||
|
|
||||||
|
**The subsequent steps are based on the Product Requirements Document, so they are created automatically**
|
||||||
|
|
||||||
|
### Step 2: Create the Functional Design Document
|
||||||
|
|
||||||
|
1. Follow the **functional-design** skill (loaded automatically)
|
||||||
|
2. Read `docs/product-requirements.md`
|
||||||
|
3. Create `docs/functional-design.md` following the skill's template and guide
|
||||||
|
|
||||||
|
### Step 3: Create the Architecture Design Document
|
||||||
|
|
||||||
|
1. Follow the **architecture-design** skill (loaded automatically)
|
||||||
|
2. Read the existing documents
|
||||||
|
3. Create `docs/architecture.md` following the skill's template and guide
|
||||||
|
|
||||||
|
### Step 4: Create the Repository Structure Document
|
||||||
|
|
||||||
|
1. Follow the **repository-structure** skill (loaded automatically)
|
||||||
|
2. Read the existing documents
|
||||||
|
3. Create `docs/repository-structure.md` following the skill's template
|
||||||
|
|
||||||
|
### Step 5: Create the Development Guidelines
|
||||||
|
|
||||||
|
1. Follow the **development-guidelines** skill (loaded automatically)
|
||||||
|
2. Read the existing documents
|
||||||
|
3. Create `docs/development-guidelines.md` following the skill's template
|
||||||
|
|
||||||
|
### Step 6: Create the Glossary
|
||||||
|
|
||||||
|
1. Follow the **glossary-creation** skill (loaded automatically)
|
||||||
|
2. Read the existing documents
|
||||||
|
3. Create `docs/glossary.md` following the skill's template
|
||||||
|
|
||||||
|
## Completion Criteria
|
||||||
|
|
||||||
|
- All six persistent documents have been created
|
||||||
|
|
||||||
|
Completion message:
|
||||||
|
```
|
||||||
|
"Initial setup is complete!
|
||||||
|
|
||||||
|
Documents created:
|
||||||
|
✅ docs/product-requirements.md
|
||||||
|
✅ docs/functional-design.md
|
||||||
|
✅ docs/architecture.md
|
||||||
|
✅ docs/repository-structure.md
|
||||||
|
✅ docs/development-guidelines.md
|
||||||
|
✅ docs/glossary.md
|
||||||
|
|
||||||
|
You are now ready to start development.
|
||||||
|
|
||||||
|
How to use going forward:
|
||||||
|
- Editing documents: just ask in normal conversation
|
||||||
|
Examples: 'Add a new feature to the PRD', 'Review architecture.md'
|
||||||
|
|
||||||
|
- Adding features: run /add-feature [feature name]
|
||||||
|
Example: /add-feature User authentication
|
||||||
|
|
||||||
|
- Document review: run /review-docs [path]
|
||||||
|
Example: /review-docs docs/product-requirements.md
|
||||||
|
"
|
||||||
|
```
|
||||||
50
.opencode/skills/architecture-design/SKILL.md
Normal file
50
.opencode/skills/architecture-design/SKILL.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
name: architecture-design
|
||||||
|
description: A detailed guide and template for creating architecture design documents. Use only when designing architecture.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Architecture Design Skill
|
||||||
|
|
||||||
|
This skill is a detailed guide for creating high-quality architecture design documents.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before starting architecture design, check the following:
|
||||||
|
|
||||||
|
### Required Documents
|
||||||
|
|
||||||
|
1. `docs/product-requirements.md` (PRD)
|
||||||
|
2. `docs/functional-design.md` (functional design document)
|
||||||
|
|
||||||
|
Architecture design defines the system structure and technology stack
|
||||||
|
needed to technically realize the PRD's requirements and functional design.
|
||||||
|
|
||||||
|
## Priority of Existing Documents
|
||||||
|
|
||||||
|
**Important**: If an existing architecture design document is present at `docs/architecture.md`,
|
||||||
|
follow this priority order:
|
||||||
|
|
||||||
|
1. **The existing architecture design document (`docs/architecture.md`)** - highest priority
|
||||||
|
- It documents project-specific technology choices and design
|
||||||
|
- It takes precedence over this skill's guide
|
||||||
|
|
||||||
|
2. **This skill's guide** - reference material
|
||||||
|
- General-purpose templates and examples
|
||||||
|
- Use when there is no existing design document, or as a supplement
|
||||||
|
|
||||||
|
**When creating new**: Refer to this skill's template and guide
|
||||||
|
**When updating**: Update while preserving the structure and content of the existing design document
|
||||||
|
|
||||||
|
## Output Destination
|
||||||
|
|
||||||
|
Save the architecture design document you create to:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/architecture.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Referencing the Template
|
||||||
|
|
||||||
|
When creating an architecture design document, use the template while referring to the following guide:
|
||||||
|
- Guide: ./guide.md
|
||||||
|
- Template: ./template.md
|
||||||
186
.opencode/skills/architecture-design/guide.md
Normal file
186
.opencode/skills/architecture-design/guide.md
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
# Architecture Design Guide
|
||||||
|
|
||||||
|
## Basic Principles
|
||||||
|
|
||||||
|
### 1. State the Rationale for Technology Choices
|
||||||
|
|
||||||
|
**Bad example**:
|
||||||
|
```
|
||||||
|
- Node.js
|
||||||
|
- TypeScript
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good example**:
|
||||||
|
```
|
||||||
|
- Node.js v24.11.0 (LTS)
|
||||||
|
- Long-term support guaranteed until April 2026, ensuring stable operation in production
|
||||||
|
- Excellent at asynchronous I/O, delivering high performance as an API server
|
||||||
|
- Rich npm ecosystem makes the required libraries easy to obtain
|
||||||
|
|
||||||
|
- TypeScript 5.x
|
||||||
|
- Static typing catches bugs at compile time, improving maintainability
|
||||||
|
- Powerful IDE autocompletion boosts development efficiency
|
||||||
|
- Shared type definitions in team development ensure code readability and quality
|
||||||
|
|
||||||
|
- npm 11.x
|
||||||
|
- Bundled with Node.js v24.11.0, so no separate installation is required
|
||||||
|
- Supports monorepo configurations via the workspaces feature
|
||||||
|
- Enables strict dependency management through package-lock.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. The Principle of Layer Separation
|
||||||
|
|
||||||
|
Clarify the responsibilities of each layer and keep dependencies unidirectional:
|
||||||
|
|
||||||
|
```
|
||||||
|
UI → Service → Data (OK)
|
||||||
|
UI ← Service (NG)
|
||||||
|
UI → Data (NG)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Measurable Requirements
|
||||||
|
|
||||||
|
Describe all performance requirements in a measurable form.
|
||||||
|
|
||||||
|
## Designing a Layered Architecture
|
||||||
|
|
||||||
|
### Responsibilities of Each Layer
|
||||||
|
|
||||||
|
**UI layer**:
|
||||||
|
```typescript
|
||||||
|
// Responsibility: Accepting and validating user input
|
||||||
|
class CLI {
|
||||||
|
// OK: Call the service layer
|
||||||
|
async addTask(title: string) {
|
||||||
|
const task = await this.taskService.create({ title });
|
||||||
|
console.log(`Created: ${task.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NG: Call the data layer directly
|
||||||
|
async addTask(title: string) {
|
||||||
|
const task = await this.repository.save({ title }); // ❌
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service layer**:
|
||||||
|
```typescript
|
||||||
|
// Responsibility: Implementing business logic
|
||||||
|
class TaskService {
|
||||||
|
// Business logic: Automatic priority estimation
|
||||||
|
async create(data: CreateTaskData): Promise<Task> {
|
||||||
|
const task = {
|
||||||
|
...data,
|
||||||
|
estimatedPriority: this.estimatePriority(data),
|
||||||
|
};
|
||||||
|
return this.repository.save(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Data layer**:
|
||||||
|
```typescript
|
||||||
|
// Responsibility: Data persistence
|
||||||
|
class TaskRepository {
|
||||||
|
async save(task: Task): Promise<void> {
|
||||||
|
await this.storage.write(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setting Performance Requirements
|
||||||
|
|
||||||
|
### Concrete Numeric Targets
|
||||||
|
|
||||||
|
```
|
||||||
|
Command execution time: within 100ms (on an average PC environment)
|
||||||
|
└─ Measurement method: measure from CLI startup to result display with console.time
|
||||||
|
└─ Measurement environment: CPU equivalent to Core i5, 8GB RAM, SSD
|
||||||
|
|
||||||
|
Task list display: within 1 second for up to 1000 items
|
||||||
|
└─ Measurement method: measure with 1000 dummy data items
|
||||||
|
└─ Acceptable range: 100ms for 100 items, 1 second for 1000 items, 10 seconds for 10000 items
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Design
|
||||||
|
|
||||||
|
### The Three Principles of Data Protection
|
||||||
|
|
||||||
|
1. **Principle of least privilege**
|
||||||
|
```bash
|
||||||
|
# File permissions
|
||||||
|
chmod 600 ~/.devtask/tasks.json # Read/write for owner only
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Input validation**
|
||||||
|
```typescript
|
||||||
|
function validateTitle(title: string): void {
|
||||||
|
if (!title || title.length === 0) {
|
||||||
|
throw new ValidationError('Title is required');
|
||||||
|
}
|
||||||
|
if (title.length > 200) {
|
||||||
|
throw new ValidationError('Title must be 200 characters or fewer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Managing sensitive information**
|
||||||
|
```bash
|
||||||
|
# Manage via environment variables
|
||||||
|
export DEVTASK_API_KEY="xxxxx" # Do not hardcode in the code
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scalability Design
|
||||||
|
|
||||||
|
### Handling Data Growth
|
||||||
|
|
||||||
|
**Expected data volume**: [e.g., 10,000 tasks]
|
||||||
|
|
||||||
|
**Countermeasures**:
|
||||||
|
- Data pagination
|
||||||
|
- Archiving old data
|
||||||
|
- Index optimization
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example archive feature: move old tasks to a separate file
|
||||||
|
class ArchiveService {
|
||||||
|
async archiveCompletedTasks(olderThan: Date): Promise<void> {
|
||||||
|
const oldTasks = await this.repository.findCompleted(olderThan);
|
||||||
|
await this.archiveStorage.save(oldTasks);
|
||||||
|
await this.repository.deleteMany(oldTasks.map(t => t.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency Management
|
||||||
|
|
||||||
|
### Version Management Policy
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^11.0.0", // Minor version upgrades are automatic
|
||||||
|
"chalk": "5.3.0" // Pin when there is a risk of breaking changes
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "~5.3.0", // Patch versions only are automatic
|
||||||
|
"eslint": "^9.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Policy**:
|
||||||
|
- Pin stable versions (allow up to minor versions with ^)
|
||||||
|
- Pin completely when there is a risk of breaking changes
|
||||||
|
- For devDependencies, automate patch versions only (~)
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Every technology choice has a stated rationale
|
||||||
|
- [ ] The layered architecture is clearly defined
|
||||||
|
- [ ] Performance requirements are measurable
|
||||||
|
- [ ] Security considerations are documented
|
||||||
|
- [ ] Scalability is taken into account
|
||||||
|
- [ ] A backup strategy is defined
|
||||||
|
- [ ] The dependency management policy is clear
|
||||||
|
- [ ] A test strategy is defined
|
||||||
153
.opencode/skills/architecture-design/template.md
Normal file
153
.opencode/skills/architecture-design/template.md
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# Architecture Design Document
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
### Languages and Runtimes
|
||||||
|
|
||||||
|
| Technology | Version |
|
||||||
|
|------|-----------|
|
||||||
|
| Node.js | v24.11.0 |
|
||||||
|
| TypeScript | 5.x |
|
||||||
|
| npm | 11.x |
|
||||||
|
|
||||||
|
### Frameworks and Libraries
|
||||||
|
|
||||||
|
| Technology | Version | Purpose | Rationale |
|
||||||
|
|------|-----------|------|----------|
|
||||||
|
| [name] | [version] | [purpose] | [rationale] |
|
||||||
|
| [name] | [version] | [purpose] | [rationale] |
|
||||||
|
|
||||||
|
### Development Tools
|
||||||
|
|
||||||
|
| Technology | Version | Purpose | Rationale |
|
||||||
|
|------|-----------|------|----------|
|
||||||
|
| [name] | [version] | [purpose] | [rationale] |
|
||||||
|
| [name] | [version] | [purpose] | [rationale] |
|
||||||
|
|
||||||
|
## Architecture Pattern
|
||||||
|
|
||||||
|
### Layered Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ UI layer │ ← Accepts and displays user input
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ Service layer │ ← Business logic
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ Data layer │ ← Data persistence
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### UI Layer
|
||||||
|
- **Responsibility**: Accepting user input, validation, displaying results
|
||||||
|
- **Permitted operations**: Calling the service layer
|
||||||
|
- **Prohibited operations**: Direct access to the data layer
|
||||||
|
|
||||||
|
#### Service Layer
|
||||||
|
- **Responsibility**: Implementing business logic, data transformation
|
||||||
|
- **Permitted operations**: Calling the data layer
|
||||||
|
- **Prohibited operations**: Depending on the UI layer
|
||||||
|
|
||||||
|
#### Data Layer
|
||||||
|
- **Responsibility**: Data persistence and retrieval
|
||||||
|
- **Permitted operations**: Access to the file system and databases
|
||||||
|
- **Prohibited operations**: Implementing business logic
|
||||||
|
|
||||||
|
## Data Persistence Strategy
|
||||||
|
|
||||||
|
### Storage Method
|
||||||
|
|
||||||
|
| Data type | Storage | Format | Rationale |
|
||||||
|
|-----------|----------|-------------|------|
|
||||||
|
| [data 1] | [method] | [format] | [rationale] |
|
||||||
|
| [data 2] | [method] | [format] | [rationale] |
|
||||||
|
|
||||||
|
### Backup Strategy
|
||||||
|
|
||||||
|
- **Frequency**: [e.g., every hour]
|
||||||
|
- **Destination**: [e.g., `.backup/` directory]
|
||||||
|
- **Generation management**: [e.g., retain the latest 5 generations]
|
||||||
|
- **Restoration method**: [procedure]
|
||||||
|
|
||||||
|
## Performance Requirements
|
||||||
|
|
||||||
|
### Response Time
|
||||||
|
|
||||||
|
| Operation | Target time | Measurement environment |
|
||||||
|
|------|---------|---------|
|
||||||
|
| [operation 1] | [time] | [environment] |
|
||||||
|
| [operation 2] | [time] | [environment] |
|
||||||
|
|
||||||
|
### Resource Usage
|
||||||
|
|
||||||
|
| Resource | Limit | Rationale |
|
||||||
|
|---------|------|------|
|
||||||
|
| Memory | [MB] | [rationale] |
|
||||||
|
| CPU | [%] | [rationale] |
|
||||||
|
| Disk | [MB] | [rationale] |
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
### Data Protection
|
||||||
|
|
||||||
|
- **Encryption**: [target data and method]
|
||||||
|
- **Access control**: [file permissions, etc.]
|
||||||
|
- **Sensitive information management**: [environment variables, config files, etc.]
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
|
||||||
|
- **Validation**: [validation items]
|
||||||
|
- **Sanitization**: [target and method]
|
||||||
|
- **Error handling**: [secure error display]
|
||||||
|
|
||||||
|
## Scalability Design
|
||||||
|
|
||||||
|
### Handling Data Growth
|
||||||
|
|
||||||
|
- **Expected data volume**: [e.g., 10,000 tasks]
|
||||||
|
- **Performance degradation countermeasures**: [method]
|
||||||
|
- **Archive strategy**: [handling of old data]
|
||||||
|
|
||||||
|
### Extensibility
|
||||||
|
|
||||||
|
- **Plugin system**: [presence/absence and design]
|
||||||
|
- **Configuration customization**: [scope of what is possible]
|
||||||
|
- **API extensibility**: [methods for future extension]
|
||||||
|
|
||||||
|
## Test Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- **Framework**: [framework name]
|
||||||
|
- **Target**: [description of test target]
|
||||||
|
- **Coverage target**: [%]
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- **Method**: [test method]
|
||||||
|
- **Target**: [description of test target]
|
||||||
|
|
||||||
|
### E2E Tests
|
||||||
|
- **Tool**: [tool name]
|
||||||
|
- **Scenarios**: [test scenarios]
|
||||||
|
|
||||||
|
## Technical Constraints
|
||||||
|
|
||||||
|
### Environment Requirements
|
||||||
|
- **OS**: [supported OS]
|
||||||
|
- **Minimum memory**: [MB]
|
||||||
|
- **Required disk space**: [MB]
|
||||||
|
- **Required external dependencies**: [list]
|
||||||
|
|
||||||
|
### Performance Constraints
|
||||||
|
- [constraint 1]
|
||||||
|
- [constraint 2]
|
||||||
|
|
||||||
|
### Security Constraints
|
||||||
|
- [constraint 1]
|
||||||
|
- [constraint 2]
|
||||||
|
|
||||||
|
## Dependency Management
|
||||||
|
|
||||||
|
| Library | Purpose | Version management policy |
|
||||||
|
|-----------|------|-------------------|
|
||||||
|
| [name] | [purpose] | [pinned/range] |
|
||||||
|
| [name] | [purpose] | [pinned/range] |
|
||||||
107
.opencode/skills/development-guidelines/SKILL.md
Normal file
107
.opencode/skills/development-guidelines/SKILL.md
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
---
|
||||||
|
name: development-guidelines
|
||||||
|
description: A comprehensive guide and template for establishing a unified development process and coding conventions across the team. Use when creating development guidelines and when implementing code.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Development Guidelines Skill
|
||||||
|
|
||||||
|
Covers the two elements needed for team development:
|
||||||
|
1. Coding conventions for implementation (implementation-guide.md)
|
||||||
|
2. Standardization of the development process (process-guide.md)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before starting to create development guidelines, check the following:
|
||||||
|
|
||||||
|
### Recommended Documents
|
||||||
|
|
||||||
|
1. `docs/architecture.md` (architecture design document) - confirm the technology stack
|
||||||
|
2. `docs/repository-structure.md` (repository structure) - confirm the directory structure
|
||||||
|
|
||||||
|
The development guidelines define concrete coding conventions and a development process
|
||||||
|
based on the project's technology stack and directory structure.
|
||||||
|
|
||||||
|
## Priority of Existing Documents
|
||||||
|
|
||||||
|
**Important**: If existing development guidelines are present at `docs/development-guidelines.md`,
|
||||||
|
follow this priority order:
|
||||||
|
|
||||||
|
1. **The existing development guidelines (`docs/development-guidelines.md`)** - highest priority
|
||||||
|
- They document project-specific conventions and processes
|
||||||
|
- They take precedence over this skill's guide
|
||||||
|
|
||||||
|
2. **This skill's guide** - reference material
|
||||||
|
- ./guides/implementation.md: general-purpose coding conventions
|
||||||
|
- ./guides/process.md: general-purpose development process
|
||||||
|
- Use when there are no existing guidelines, or as a supplement
|
||||||
|
|
||||||
|
**When creating new**: Refer to this skill's guides and template
|
||||||
|
**When updating**: Update while preserving the structure and content of the existing guidelines
|
||||||
|
|
||||||
|
## Output Destination
|
||||||
|
|
||||||
|
Save the development guidelines you create to:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/development-guidelines.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### When Implementing Code
|
||||||
|
Rules and conventions for code implementation: ./guides/implementation.md
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
- TypeScript/JavaScript conventions
|
||||||
|
- Type definitions and naming conventions
|
||||||
|
- Function design and error handling
|
||||||
|
- Comment conventions
|
||||||
|
- Security and performance
|
||||||
|
- Test code implementation
|
||||||
|
- Refactoring techniques
|
||||||
|
|
||||||
|
### When Referencing/Defining the Development Process
|
||||||
|
Git workflow, test strategy, code review: ./guides/process.md
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
- Basic principles (the importance of concrete examples, explaining rationale)
|
||||||
|
- Git workflow rules (Git Flow branching strategy)
|
||||||
|
- Commit messages and the PR process
|
||||||
|
- Test strategy (pyramid and coverage)
|
||||||
|
- The code review process
|
||||||
|
- Quality automation
|
||||||
|
|
||||||
|
### Template
|
||||||
|
When creating development guidelines: ./template.md
|
||||||
|
|
||||||
|
|
||||||
|
## Guide by Use Case
|
||||||
|
|
||||||
|
### When Developing New Code
|
||||||
|
1. Confirm naming conventions and coding standards in ./guides/implementation.md
|
||||||
|
2. Confirm the branching strategy and PR handling in ./guides/process.md
|
||||||
|
3. Write tests first (TDD)
|
||||||
|
|
||||||
|
### During Code Review
|
||||||
|
- Refer to "The Code Review Process" in ./guides/process.md
|
||||||
|
- Check for convention violations against ./guides/implementation.md
|
||||||
|
|
||||||
|
### When Designing Tests
|
||||||
|
- "Test Strategy" in ./guides/process.md (pyramid, coverage)
|
||||||
|
- "Test Code" in ./guides/implementation.md (implementation patterns)
|
||||||
|
|
||||||
|
### When Preparing a Release
|
||||||
|
- "Git Workflow Rules" in ./guides/process.md (policy for merging into main)
|
||||||
|
- Confirm that commit messages follow Conventional Commits
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Coding conventions are defined with concrete examples
|
||||||
|
- [ ] Naming conventions are clear (per language and project-specific)
|
||||||
|
- [ ] An error handling policy is defined
|
||||||
|
- [ ] A branching strategy is decided (Git Flow recommended)
|
||||||
|
- [ ] Commit message conventions are clear
|
||||||
|
- [ ] A PR template is prepared
|
||||||
|
- [ ] Test types and coverage targets are set
|
||||||
|
- [ ] A code review process is defined
|
||||||
|
- [ ] A CI/CD pipeline is established
|
||||||
627
.opencode/skills/development-guidelines/guides/implementation.md
Normal file
627
.opencode/skills/development-guidelines/guides/implementation.md
Normal file
@ -0,0 +1,627 @@
|
|||||||
|
# Implementation Guide
|
||||||
|
|
||||||
|
## TypeScript/JavaScript Conventions
|
||||||
|
|
||||||
|
### Type Definitions
|
||||||
|
|
||||||
|
**Using built-in types**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Use built-in types
|
||||||
|
function processItems(items: string[]): Record<string, number> {
|
||||||
|
return items.reduce((acc, item) => {
|
||||||
|
acc[item] = (acc[item] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, number>);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Importing from the typing module
|
||||||
|
import { List, Dict } from 'typing';
|
||||||
|
function processItems(items: List[str]): Dict[str, int] { }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Type annotation principles**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Explicit type annotations
|
||||||
|
function calculateTotal(prices: number[]): number {
|
||||||
|
return prices.reduce((sum, price) => sum + price, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Over-relying on type inference
|
||||||
|
function calculateTotal(prices) { // becomes the any type
|
||||||
|
return prices.reduce((sum, price) => sum + price, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Interface vs. type alias**:
|
||||||
|
```typescript
|
||||||
|
// Interface: extensible object types
|
||||||
|
interface Task {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
completed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension
|
||||||
|
interface ExtendedTask extends Task {
|
||||||
|
priority: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type alias: union types, primitive types, etc.
|
||||||
|
type TaskStatus = 'todo' | 'in_progress' | 'completed';
|
||||||
|
type TaskId = string;
|
||||||
|
type Nullable<T> = T | null;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
**Variables and functions**:
|
||||||
|
```typescript
|
||||||
|
// Variables: camelCase, nouns
|
||||||
|
const userName = 'John';
|
||||||
|
const taskList = [];
|
||||||
|
const isCompleted = true;
|
||||||
|
|
||||||
|
// Functions: camelCase, start with a verb
|
||||||
|
function fetchUserData() { }
|
||||||
|
function validateEmail(email: string) { }
|
||||||
|
function calculateTotalPrice(items: Item[]) { }
|
||||||
|
|
||||||
|
// Boolean: start with is, has, should, or can
|
||||||
|
const isValid = true;
|
||||||
|
const hasPermission = false;
|
||||||
|
const shouldRetry = true;
|
||||||
|
const canDelete = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Classes and interfaces**:
|
||||||
|
```typescript
|
||||||
|
// Classes: PascalCase, nouns
|
||||||
|
class TaskManager { }
|
||||||
|
class UserAuthenticationService { }
|
||||||
|
|
||||||
|
// Interfaces: PascalCase
|
||||||
|
interface TaskRepository { }
|
||||||
|
interface UserProfile { }
|
||||||
|
|
||||||
|
// Type aliases: PascalCase
|
||||||
|
type TaskStatus = 'todo' | 'in_progress' | 'completed';
|
||||||
|
```
|
||||||
|
|
||||||
|
**Constants**:
|
||||||
|
```typescript
|
||||||
|
// UPPER_SNAKE_CASE
|
||||||
|
const MAX_RETRY_COUNT = 3;
|
||||||
|
const API_BASE_URL = 'https://api.example.com';
|
||||||
|
const DEFAULT_TIMEOUT = 5000;
|
||||||
|
|
||||||
|
// For configuration objects
|
||||||
|
const CONFIG = {
|
||||||
|
maxRetryCount: 3,
|
||||||
|
apiBaseUrl: 'https://api.example.com',
|
||||||
|
defaultTimeout: 5000,
|
||||||
|
} as const;
|
||||||
|
```
|
||||||
|
|
||||||
|
**File names**:
|
||||||
|
```typescript
|
||||||
|
// Class files: PascalCase
|
||||||
|
// TaskService.ts
|
||||||
|
// UserRepository.ts
|
||||||
|
|
||||||
|
// Functions and utilities: camelCase
|
||||||
|
// formatDate.ts
|
||||||
|
// validateEmail.ts
|
||||||
|
|
||||||
|
// Components (React, etc.): PascalCase
|
||||||
|
// TaskList.tsx
|
||||||
|
// UserProfile.tsx
|
||||||
|
|
||||||
|
// Constants: kebab-case or UPPER_SNAKE_CASE
|
||||||
|
// api-endpoints.ts
|
||||||
|
// ERROR_MESSAGES.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Function Design
|
||||||
|
|
||||||
|
**Single responsibility principle**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: A single responsibility
|
||||||
|
function calculateTotalPrice(items: CartItem[]): number {
|
||||||
|
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPrice(amount: number): string {
|
||||||
|
return `¥${amount.toLocaleString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Multiple responsibilities
|
||||||
|
function calculateAndFormatPrice(items: CartItem[]): string {
|
||||||
|
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||||||
|
return `¥${total.toLocaleString()}`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Function length**:
|
||||||
|
- Target: within 20 lines
|
||||||
|
- Recommended: within 50 lines
|
||||||
|
- 100 lines or more: consider refactoring
|
||||||
|
|
||||||
|
**Number of parameters**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Group into an object
|
||||||
|
interface CreateTaskOptions {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
priority?: 'high' | 'medium' | 'low';
|
||||||
|
dueDate?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTask(options: CreateTaskOptions): Task {
|
||||||
|
// implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Too many parameters
|
||||||
|
function createTask(
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
priority: string,
|
||||||
|
dueDate: Date,
|
||||||
|
tags: string[],
|
||||||
|
assignee: string
|
||||||
|
): Task {
|
||||||
|
// implementation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
**Custom error classes**:
|
||||||
|
```typescript
|
||||||
|
// Defining error classes
|
||||||
|
class ValidationError extends Error {
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
public field: string,
|
||||||
|
public value: unknown
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ValidationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotFoundError extends Error {
|
||||||
|
constructor(
|
||||||
|
public resource: string,
|
||||||
|
public id: string
|
||||||
|
) {
|
||||||
|
super(`${resource} not found: ${id}`);
|
||||||
|
this.name = 'NotFoundError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseError extends Error {
|
||||||
|
constructor(message: string, public cause?: Error) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'DatabaseError';
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error handling patterns**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Proper error handling
|
||||||
|
async function getTask(id: string): Promise<Task> {
|
||||||
|
try {
|
||||||
|
const task = await repository.findById(id);
|
||||||
|
|
||||||
|
if (!task) {
|
||||||
|
throw new NotFoundError('Task', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return task;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
// Expected error: handle appropriately
|
||||||
|
logger.warn(`Task not found: ${id}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unexpected error: wrap and propagate upward
|
||||||
|
throw new DatabaseError('Failed to retrieve the task', error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Ignoring the error
|
||||||
|
async function getTask(id: string): Promise<Task | null> {
|
||||||
|
try {
|
||||||
|
return await repository.findById(id);
|
||||||
|
} catch (error) {
|
||||||
|
return null; // error information is lost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error messages**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Specific and suggests a solution
|
||||||
|
throw new ValidationError(
|
||||||
|
'Title must be 1-200 characters. Current length: 250',
|
||||||
|
'title',
|
||||||
|
title
|
||||||
|
);
|
||||||
|
|
||||||
|
// ❌ Bad: Vague and unhelpful
|
||||||
|
throw new Error('Invalid input');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Asynchronous Processing
|
||||||
|
|
||||||
|
**Using async/await**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: async/await
|
||||||
|
async function fetchUserTasks(userId: string): Promise<Task[]> {
|
||||||
|
try {
|
||||||
|
const user = await userRepository.findById(userId);
|
||||||
|
const tasks = await taskRepository.findByUserId(user.id);
|
||||||
|
return tasks;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to retrieve tasks', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Promise chains
|
||||||
|
function fetchUserTasks(userId: string): Promise<Task[]> {
|
||||||
|
return userRepository.findById(userId)
|
||||||
|
.then(user => taskRepository.findByUserId(user.id))
|
||||||
|
.then(tasks => tasks)
|
||||||
|
.catch(error => {
|
||||||
|
logger.error('Failed to retrieve tasks', error);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parallel processing**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Run in parallel with Promise.all
|
||||||
|
async function fetchMultipleUsers(ids: string[]): Promise<User[]> {
|
||||||
|
const promises = ids.map(id => userRepository.findById(id));
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Sequential execution
|
||||||
|
async function fetchMultipleUsers(ids: string[]): Promise<User[]> {
|
||||||
|
const users: User[] = [];
|
||||||
|
for (const id of ids) {
|
||||||
|
const user = await userRepository.findById(id); // slow
|
||||||
|
users.push(user);
|
||||||
|
}
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comment Conventions
|
||||||
|
|
||||||
|
### Documentation Comments
|
||||||
|
|
||||||
|
**TSDoc format**:
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Creates a task
|
||||||
|
*
|
||||||
|
* @param data - Data for the task to create
|
||||||
|
* @returns The created task
|
||||||
|
* @throws {ValidationError} When the data is invalid
|
||||||
|
* @throws {DatabaseError} When a database error occurs
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const task = await createTask({
|
||||||
|
* title: 'New task',
|
||||||
|
* priority: 'high'
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async function createTask(data: CreateTaskData): Promise<Task> {
|
||||||
|
// implementation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inline Comments
|
||||||
|
|
||||||
|
**Good comments**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Explain the reason
|
||||||
|
// Invalidate the cache to fetch the latest data
|
||||||
|
cache.clear();
|
||||||
|
|
||||||
|
// ✅ Explain complex logic
|
||||||
|
// Compute the maximum subarray sum using Kadane's algorithm
|
||||||
|
// Time complexity: O(n)
|
||||||
|
let maxSoFar = arr[0];
|
||||||
|
let maxEndingHere = arr[0];
|
||||||
|
|
||||||
|
// ✅ Make use of TODO and FIXME
|
||||||
|
// TODO: Implement caching (Issue #123)
|
||||||
|
// FIXME: Performance degrades with large datasets (Issue #456)
|
||||||
|
// HACK: Temporary workaround, needs refactoring later
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bad comments**:
|
||||||
|
```typescript
|
||||||
|
// ❌ Merely restating the code
|
||||||
|
// Increment i by 1
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// ❌ Stale information
|
||||||
|
// This code was added in 2020 (unnecessary information)
|
||||||
|
|
||||||
|
// ❌ Commented-out code
|
||||||
|
// const oldImplementation = () => { ... }; // should be deleted
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Strict validation
|
||||||
|
function validateEmail(email: string): void {
|
||||||
|
if (!email || typeof email !== 'string') {
|
||||||
|
throw new ValidationError('Email address is required', 'email', email);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(email)) {
|
||||||
|
throw new ValidationError('Email address format is invalid', 'email', email);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (email.length > 254) {
|
||||||
|
throw new ValidationError('Email address is too long', 'email', email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: No validation
|
||||||
|
function validateEmail(email: string): void {
|
||||||
|
// no validation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Sensitive Information
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Load from environment variables
|
||||||
|
import { config } from './config';
|
||||||
|
|
||||||
|
const apiKey = process.env.API_KEY;
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new Error('The API_KEY environment variable is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Hardcoding
|
||||||
|
const apiKey = 'sk-1234567890abcdef'; // never do this!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
### Choosing Data Structures
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: O(1) access with a Map
|
||||||
|
const userMap = new Map(users.map(u => [u.id, u]));
|
||||||
|
const user = userMap.get(userId); // O(1)
|
||||||
|
|
||||||
|
// ❌ Bad: O(n) search with an array
|
||||||
|
const user = users.find(u => u.id === userId); // O(n)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loop Optimization
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Move unnecessary computation outside the loop
|
||||||
|
const length = items.length;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
process(items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Recomputing length every iteration
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
process(items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Better: Use for...of
|
||||||
|
for (const item of items) {
|
||||||
|
process(item);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memoization
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Caching computation results
|
||||||
|
const cache = new Map<string, Result>();
|
||||||
|
|
||||||
|
function expensiveCalculation(input: string): Result {
|
||||||
|
if (cache.has(input)) {
|
||||||
|
return cache.get(input)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = /* heavy computation */;
|
||||||
|
cache.set(input, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Code
|
||||||
|
|
||||||
|
### Test Structure (Given-When-Then)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
describe('TaskService', () => {
|
||||||
|
describe('create', () => {
|
||||||
|
it('can create a task with valid data', async () => {
|
||||||
|
// Given: setup
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
const taskData = {
|
||||||
|
title: 'Test task',
|
||||||
|
description: 'Description for testing',
|
||||||
|
};
|
||||||
|
|
||||||
|
// When: execute
|
||||||
|
const result = await service.create(taskData);
|
||||||
|
|
||||||
|
// Then: verify
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result.id).toBeDefined();
|
||||||
|
expect(result.title).toBe('Test task');
|
||||||
|
expect(result.description).toBe('Description for testing');
|
||||||
|
expect(result.createdAt).toBeInstanceOf(Date);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws a ValidationError when the title is empty', async () => {
|
||||||
|
// Given: setup
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
const invalidData = { title: '' };
|
||||||
|
|
||||||
|
// When/Then: execute and verify
|
||||||
|
await expect(
|
||||||
|
service.create(invalidData)
|
||||||
|
).rejects.toThrow(ValidationError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creating Mocks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Mocks based on an interface
|
||||||
|
const mockRepository: TaskRepository = {
|
||||||
|
save: jest.fn(),
|
||||||
|
findById: jest.fn(),
|
||||||
|
findAll: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure behavior per test
|
||||||
|
beforeEach(() => {
|
||||||
|
mockRepository.findById = jest.fn((id) => {
|
||||||
|
if (id === 'existing-id') {
|
||||||
|
return Promise.resolve(mockTask);
|
||||||
|
}
|
||||||
|
return Promise.resolve(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Refactoring
|
||||||
|
|
||||||
|
### Eliminating Magic Numbers
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Define constants
|
||||||
|
const MAX_RETRY_COUNT = 3;
|
||||||
|
const RETRY_DELAY_MS = 1000;
|
||||||
|
|
||||||
|
for (let i = 0; i < MAX_RETRY_COUNT; i++) {
|
||||||
|
try {
|
||||||
|
return await fetchData();
|
||||||
|
} catch (error) {
|
||||||
|
if (i < MAX_RETRY_COUNT - 1) {
|
||||||
|
await sleep(RETRY_DELAY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: Magic numbers
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
try {
|
||||||
|
return await fetchData();
|
||||||
|
} catch (error) {
|
||||||
|
if (i < 2) {
|
||||||
|
await sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extracting Functions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Extract functions
|
||||||
|
function processOrder(order: Order): void {
|
||||||
|
validateOrder(order);
|
||||||
|
calculateTotal(order);
|
||||||
|
applyDiscounts(order);
|
||||||
|
saveOrder(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateOrder(order: Order): void {
|
||||||
|
if (!order.items || order.items.length === 0) {
|
||||||
|
throw new ValidationError('No items selected', 'items', order.items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateTotal(order: Order): void {
|
||||||
|
order.total = order.items.reduce(
|
||||||
|
(sum, item) => sum + item.price * item.quantity,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad: A long function
|
||||||
|
function processOrder(order: Order): void {
|
||||||
|
if (!order.items || order.items.length === 0) {
|
||||||
|
throw new ValidationError('No items selected', 'items', order.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
order.total = order.items.reduce(
|
||||||
|
(sum, item) => sum + item.price * item.quantity,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (order.coupon) {
|
||||||
|
order.total -= order.total * order.coupon.discountRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.save(order);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
Confirm before completing implementation:
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
- [ ] Naming is clear and consistent
|
||||||
|
- [ ] Functions have a single responsibility
|
||||||
|
- [ ] No magic numbers
|
||||||
|
- [ ] Type annotations are written appropriately
|
||||||
|
- [ ] Error handling is implemented
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- [ ] Input validation is implemented
|
||||||
|
- [ ] No sensitive information is hardcoded
|
||||||
|
- [ ] SQL injection countermeasures are in place
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- [ ] Appropriate data structures are used
|
||||||
|
- [ ] Unnecessary computation is avoided
|
||||||
|
- [ ] Loops are optimized
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- [ ] Unit tests are written
|
||||||
|
- [ ] Tests pass
|
||||||
|
- [ ] Edge cases are covered
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [ ] Functions and classes have TSDoc comments
|
||||||
|
- [ ] Complex logic has comments
|
||||||
|
- [ ] TODOs and FIXMEs are noted (where applicable)
|
||||||
|
|
||||||
|
### Tooling
|
||||||
|
- [ ] No lint errors
|
||||||
|
- [ ] Type checking passes
|
||||||
|
- [ ] Formatting is consistent
|
||||||
421
.opencode/skills/development-guidelines/guides/process.md
Normal file
421
.opencode/skills/development-guidelines/guides/process.md
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
# Process Guide
|
||||||
|
|
||||||
|
## Basic Principles
|
||||||
|
|
||||||
|
### 1. Include Plenty of Concrete Examples
|
||||||
|
|
||||||
|
Present concrete code examples, not just abstract rules.
|
||||||
|
|
||||||
|
**Bad example**:
|
||||||
|
```
|
||||||
|
Make variable names easy to understand
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good example**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: The role is clear
|
||||||
|
const userAuthentication = new UserAuthenticationService();
|
||||||
|
const taskRepository = new TaskRepository();
|
||||||
|
|
||||||
|
// ❌ Bad: Ambiguous
|
||||||
|
const auth = new Service();
|
||||||
|
const repo = new Repository();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Explain the Reasoning
|
||||||
|
|
||||||
|
Make clear "why we do it this way."
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
## Don't Ignore Errors
|
||||||
|
|
||||||
|
Reason: Ignoring errors makes it difficult to identify the cause of a problem.
|
||||||
|
Handle expected errors appropriately, and propagate unexpected errors upward
|
||||||
|
so they can be recorded in the logs.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Set Measurable Criteria
|
||||||
|
|
||||||
|
Avoid vague expressions and provide concrete numbers.
|
||||||
|
|
||||||
|
**Bad example**:
|
||||||
|
```
|
||||||
|
Keep code coverage high
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good example**:
|
||||||
|
```
|
||||||
|
Code coverage targets:
|
||||||
|
- Unit tests: 80% or higher
|
||||||
|
- Integration tests: 60% or higher
|
||||||
|
- E2E tests: 100% of the main flows
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git Operation Rules
|
||||||
|
|
||||||
|
### Branching Strategy (Adopting Git Flow)
|
||||||
|
|
||||||
|
**What is Git Flow**:
|
||||||
|
A branching model proposed by Vincent Driessen that systematically manages feature development, releases, and hotfixes. With clearly divided roles, it enables parallel work and stable releases in team development.
|
||||||
|
|
||||||
|
**Branch structure**:
|
||||||
|
```
|
||||||
|
main (production environment)
|
||||||
|
└── develop (development/integration environment)
|
||||||
|
├── feature/* (new feature development)
|
||||||
|
├── fix/* (bug fixes)
|
||||||
|
└── release/* (release preparation) *as needed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Operation rules**:
|
||||||
|
- **main**: Holds only stable code already released to production. Versioned with tags
|
||||||
|
- **develop**: Integrates the latest development code for the next release. Automated tests run in CI
|
||||||
|
- **feature/\*, fix/\***: Branch from develop, and merge into develop via a PR after the work is complete
|
||||||
|
- **No direct commits**: Require PR review on all branches to ensure code quality
|
||||||
|
- **Merge policy**: Recommend squash merge for feature→develop, and a merge commit for develop→main
|
||||||
|
|
||||||
|
**Benefits of Git Flow**:
|
||||||
|
- Branch roles are clear, making parallel development by multiple people easier
|
||||||
|
- The production environment (main) is always kept clean
|
||||||
|
- During emergencies, hotfix branches enable rapid response (introduce as needed)
|
||||||
|
|
||||||
|
### Commit Message Conventions
|
||||||
|
|
||||||
|
**Conventional Commits is recommended**:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
**List of types**:
|
||||||
|
```
|
||||||
|
feat: New feature (minor version up)
|
||||||
|
fix: Bug fix (patch version up)
|
||||||
|
docs: Documentation
|
||||||
|
style: Formatting (no effect on code behavior)
|
||||||
|
refactor: Refactoring
|
||||||
|
perf: Performance improvement
|
||||||
|
test: Adding or fixing tests
|
||||||
|
build: Build system
|
||||||
|
ci: CI/CD configuration
|
||||||
|
chore: Other (dependency updates, etc.)
|
||||||
|
|
||||||
|
BREAKING CHANGE: Breaking change (major version up)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example of a good commit message**:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(task): Add priority-setting feature
|
||||||
|
|
||||||
|
Users can now set a priority (high/medium/low) on tasks.
|
||||||
|
|
||||||
|
Implementation details:
|
||||||
|
- Added a priority field to the Task model
|
||||||
|
- Added a --priority option to the CLI
|
||||||
|
- Implemented sorting by priority
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
- The structure of the Task type has changed
|
||||||
|
- Existing task data requires migration
|
||||||
|
|
||||||
|
Closes #123
|
||||||
|
BREAKING CHANGE: Added a required priority field to the Task type
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pull Request Template
|
||||||
|
|
||||||
|
**An effective PR template**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Type of Change
|
||||||
|
- [ ] New feature (feat)
|
||||||
|
- [ ] Bug fix (fix)
|
||||||
|
- [ ] Refactoring (refactor)
|
||||||
|
- [ ] Documentation (docs)
|
||||||
|
- [ ] Other (chore)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
### What was changed
|
||||||
|
[Brief description]
|
||||||
|
|
||||||
|
### Why it was changed
|
||||||
|
[Background and reasoning]
|
||||||
|
|
||||||
|
### How it was changed
|
||||||
|
- [Change 1]
|
||||||
|
- [Change 2]
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
### Tests performed
|
||||||
|
- [ ] Added unit tests
|
||||||
|
- [ ] Added integration tests
|
||||||
|
- [ ] Performed manual testing
|
||||||
|
|
||||||
|
### Test results
|
||||||
|
[Description of the test results]
|
||||||
|
|
||||||
|
## Related Issues
|
||||||
|
Closes #[number]
|
||||||
|
Refs #[number]
|
||||||
|
|
||||||
|
## Review Points
|
||||||
|
[Points you especially want reviewers to look at]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Strategy
|
||||||
|
|
||||||
|
### Test Pyramid
|
||||||
|
|
||||||
|
```
|
||||||
|
/\
|
||||||
|
/E2E\ Few (slow, high cost)
|
||||||
|
/------\
|
||||||
|
/ Integ. \ Medium
|
||||||
|
/----------\
|
||||||
|
/ Unit \ Many (fast, low cost)
|
||||||
|
/--------------\
|
||||||
|
```
|
||||||
|
|
||||||
|
**Target ratio**:
|
||||||
|
- Unit tests: 70%
|
||||||
|
- Integration tests: 20%
|
||||||
|
- E2E tests: 10%
|
||||||
|
|
||||||
|
### How to Write Tests
|
||||||
|
|
||||||
|
**Given-When-Then pattern**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
describe('TaskService', () => {
|
||||||
|
describe('task creation', () => {
|
||||||
|
it('can create a task with valid data', async () => {
|
||||||
|
// Given: setup
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
const validData = { title: 'Test' };
|
||||||
|
|
||||||
|
// When: execute
|
||||||
|
const result = await service.create(validData);
|
||||||
|
|
||||||
|
// Then: verify
|
||||||
|
expect(result.id).toBeDefined();
|
||||||
|
expect(result.title).toBe('Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws a ValidationError when the title is empty', async () => {
|
||||||
|
// Given: setup
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
const invalidData = { title: '' };
|
||||||
|
|
||||||
|
// When/Then: execute and verify
|
||||||
|
await expect(
|
||||||
|
service.create(invalidData)
|
||||||
|
).rejects.toThrow(ValidationError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Coverage Targets
|
||||||
|
|
||||||
|
**Measurable targets**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
// jest.config.js
|
||||||
|
{
|
||||||
|
"coverageThreshold": {
|
||||||
|
"global": {
|
||||||
|
"branches": 80,
|
||||||
|
"functions": 80,
|
||||||
|
"lines": 80,
|
||||||
|
"statements": 80
|
||||||
|
},
|
||||||
|
"./src/services/": {
|
||||||
|
"branches": 90,
|
||||||
|
"functions": 90,
|
||||||
|
"lines": 90,
|
||||||
|
"statements": 90
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reasoning**:
|
||||||
|
- Critical business logic (services/) requires high coverage
|
||||||
|
- The UI layer is acceptable at a lower level
|
||||||
|
- Don't aim for 100% (balance cost and benefit)
|
||||||
|
|
||||||
|
## Code Review Process
|
||||||
|
|
||||||
|
### Purpose of Review
|
||||||
|
|
||||||
|
1. **Quality assurance**: Early detection of bugs
|
||||||
|
2. **Knowledge sharing**: Understanding the codebase across the whole team
|
||||||
|
3. **Learning opportunity**: Sharing best practices
|
||||||
|
|
||||||
|
### Points for Effective Reviews
|
||||||
|
|
||||||
|
**For reviewers**:
|
||||||
|
|
||||||
|
1. **Constructive feedback**
|
||||||
|
```markdown
|
||||||
|
## ❌ Bad example
|
||||||
|
This code is no good.
|
||||||
|
|
||||||
|
## ✅ Good example
|
||||||
|
This implementation results in O(n²) time complexity.
|
||||||
|
Using a Map can improve it to O(n):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const taskMap = new Map(tasks.map(t => [t.id, t]));
|
||||||
|
const result = ids.map(id => taskMap.get(id));
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **State the priority explicitly**
|
||||||
|
```markdown
|
||||||
|
[Required] Security: A password is being output to the logs
|
||||||
|
[Recommended] Performance: Avoid DB calls inside a loop
|
||||||
|
[Suggestion] Readability: Could this function name be made clearer?
|
||||||
|
[Question] Could you explain the intent of this processing?
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Positive feedback too**
|
||||||
|
```markdown
|
||||||
|
✨ This implementation is easy to understand!
|
||||||
|
👍 Edge cases are thoroughly considered
|
||||||
|
💡 This pattern looks reusable elsewhere
|
||||||
|
```
|
||||||
|
|
||||||
|
**For reviewees**:
|
||||||
|
|
||||||
|
1. **Perform a self-review**
|
||||||
|
- Review your own code before creating the PR
|
||||||
|
- Add comments where explanation is needed
|
||||||
|
|
||||||
|
2. **Aim for small PRs**
|
||||||
|
- 1 PR = 1 feature
|
||||||
|
- Number of changed files: 10 or fewer recommended
|
||||||
|
- Number of changed lines: 300 or fewer recommended
|
||||||
|
|
||||||
|
3. **Explain carefully**
|
||||||
|
- Why you chose this implementation
|
||||||
|
- Alternatives you considered
|
||||||
|
- Points you especially want reviewed
|
||||||
|
|
||||||
|
### Review Time Guidelines
|
||||||
|
|
||||||
|
- Small PR (100 lines or fewer): 15 minutes
|
||||||
|
- Medium PR (100-300 lines): 30 minutes
|
||||||
|
- Large PR (300 lines or more): 1 hour or more
|
||||||
|
|
||||||
|
**Principle**: Avoid large PRs; split them up
|
||||||
|
|
||||||
|
## Promoting Automation (Where Applicable)
|
||||||
|
|
||||||
|
### Automating Quality Checks
|
||||||
|
|
||||||
|
**Automation items and the tools adopted**:
|
||||||
|
|
||||||
|
1. **Lint checks**
|
||||||
|
- **ESLint 9.x** + **@typescript-eslint**
|
||||||
|
- Unifies coding conventions with a TypeScript-specific rule set
|
||||||
|
- Automatically detects potential bugs and deprecated patterns
|
||||||
|
- Configuration file: `eslint.config.js` (Flat Config format)
|
||||||
|
|
||||||
|
2. **Code formatting**
|
||||||
|
- **Prettier 3.x**
|
||||||
|
- Automatically formats code style, reducing debate during review
|
||||||
|
- Used together with ESLint, avoiding conflicts via `eslint-config-prettier`
|
||||||
|
- Configuration file: `.prettierrc`
|
||||||
|
|
||||||
|
3. **Type checking**
|
||||||
|
- **TypeScript Compiler (tsc) 5.x**
|
||||||
|
- Checks only type errors with `tsc --noEmit`
|
||||||
|
- Verifies type safety independently of the build
|
||||||
|
- Configuration file: `tsconfig.json`
|
||||||
|
|
||||||
|
4. **Running tests**
|
||||||
|
- **Vitest 2.x**
|
||||||
|
- Fast startup and execution based on Vite
|
||||||
|
- Natively supports TypeScript/ESM and works with zero configuration
|
||||||
|
- Coverage measurement (@vitest/coverage-v8) is built in as standard
|
||||||
|
- Modern development experience with HMR support
|
||||||
|
|
||||||
|
5. **Build verification**
|
||||||
|
- **TypeScript Compiler (tsc)**
|
||||||
|
- Guarantees a type-checked build with the standard compiler
|
||||||
|
- Simple configuration with no additional tools required
|
||||||
|
- Centralizes output settings in `tsconfig.json`
|
||||||
|
|
||||||
|
**Implementation methods**:
|
||||||
|
|
||||||
|
**1. CI/CD (GitHub Actions)**
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/ci.yml
|
||||||
|
name: CI
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run typecheck
|
||||||
|
- run: npm run test
|
||||||
|
- run: npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Pre-commit hooks (Husky 9.x + lint-staged)**
|
||||||
|
```json
|
||||||
|
// package.json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "husky",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"build": "tsc"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
# .husky/pre-commit
|
||||||
|
npm run lint-staged
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
**Effects of adoption**:
|
||||||
|
- Automated checks run before committing, preventing defective code from being introduced
|
||||||
|
- CI runs automatically when a PR is created, ensuring quality before merging
|
||||||
|
- Early detection reduces fix costs by up to 80% (compared to when bugs are found after production)
|
||||||
|
|
||||||
|
**Why this configuration was chosen**:
|
||||||
|
- A standard and modern configuration in the TypeScript ecosystem as of 2025
|
||||||
|
- High compatibility between tools, with few configuration conflicts
|
||||||
|
- An excellent balance between development experience and execution speed
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] A branching strategy is decided
|
||||||
|
- [ ] Commit message conventions are clear
|
||||||
|
- [ ] A PR template is prepared
|
||||||
|
- [ ] Test types and coverage targets are set
|
||||||
|
- [ ] A code review process is defined
|
||||||
|
- [ ] A CI/CD pipeline is built
|
||||||
410
.opencode/skills/development-guidelines/template.md
Normal file
410
.opencode/skills/development-guidelines/template.md
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
# Development Guidelines
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
#### Variables and Functions
|
||||||
|
|
||||||
|
**TypeScript/JavaScript**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
const userProfileData = fetchUserProfile();
|
||||||
|
function calculateTotalPrice(items: CartItem[]): number { }
|
||||||
|
|
||||||
|
// ❌ Bad example
|
||||||
|
const data = fetch();
|
||||||
|
function calc(arr: any[]): number { }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Principles**:
|
||||||
|
- Variables: camelCase, noun or noun phrase
|
||||||
|
- Functions: camelCase, start with a verb
|
||||||
|
- Constants: UPPER_SNAKE_CASE
|
||||||
|
- Boolean: start with `is`, `has`, or `should`
|
||||||
|
|
||||||
|
#### Classes and Interfaces
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Class: PascalCase, noun
|
||||||
|
class TaskManager { }
|
||||||
|
class UserAuthenticationService { }
|
||||||
|
|
||||||
|
// Interface: PascalCase, with or without the I prefix
|
||||||
|
interface ITaskRepository { }
|
||||||
|
interface Task { }
|
||||||
|
|
||||||
|
// Type alias: PascalCase
|
||||||
|
type TaskStatus = 'todo' | 'in_progress' | 'completed';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Formatting
|
||||||
|
|
||||||
|
**Indentation**: [2 spaces / 4 spaces / tabs]
|
||||||
|
|
||||||
|
**Line length**: maximum [80/100/120] characters
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
// [language] code formatting example
|
||||||
|
[code example]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comment Conventions
|
||||||
|
|
||||||
|
**Function and class documentation**:
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Calculate the total number of tasks
|
||||||
|
*
|
||||||
|
* @param tasks - Array of tasks to count
|
||||||
|
* @param filter - Filter condition (optional)
|
||||||
|
* @returns Total number of tasks
|
||||||
|
* @throws {ValidationError} When the task array is invalid
|
||||||
|
*/
|
||||||
|
function countTasks(
|
||||||
|
tasks: Task[],
|
||||||
|
filter?: TaskFilter
|
||||||
|
): number {
|
||||||
|
// Implementation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Inline comments**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example: explain why you do it
|
||||||
|
// Invalidate the cache to fetch the latest data
|
||||||
|
cache.clear();
|
||||||
|
|
||||||
|
// ❌ Bad example: what you are doing (obvious from the code)
|
||||||
|
// Clear the cache
|
||||||
|
cache.clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
**Principles**:
|
||||||
|
- Expected errors: define an appropriate error class
|
||||||
|
- Unexpected errors: propagate upward
|
||||||
|
- Do not ignore errors
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
// Error class definition
|
||||||
|
class ValidationError extends Error {
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
public field: string,
|
||||||
|
public value: unknown
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ValidationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
try {
|
||||||
|
const task = await taskService.create(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
console.error(`Validation error [${error.field}]: ${error.message}`);
|
||||||
|
// Provide feedback to the user
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected error:', error);
|
||||||
|
throw error; // Propagate upward
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git Workflow Rules
|
||||||
|
|
||||||
|
### Branching Strategy
|
||||||
|
|
||||||
|
**Branch types**:
|
||||||
|
- `main`: ready to deploy to production
|
||||||
|
- `develop`: latest development state
|
||||||
|
- `feature/[feature-name]`: new feature development
|
||||||
|
- `fix/[fix-description]`: bug fixes
|
||||||
|
- `refactor/[target]`: refactoring
|
||||||
|
|
||||||
|
**Flow**:
|
||||||
|
```
|
||||||
|
main
|
||||||
|
└─ develop
|
||||||
|
├─ feature/task-management
|
||||||
|
├─ feature/user-auth
|
||||||
|
└─ fix/task-validation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Message Conventions
|
||||||
|
|
||||||
|
**Format**:
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Type**:
|
||||||
|
- `feat`: new feature
|
||||||
|
- `fix`: bug fix
|
||||||
|
- `docs`: documentation
|
||||||
|
- `style`: code formatting
|
||||||
|
- `refactor`: refactoring
|
||||||
|
- `test`: adding or fixing tests
|
||||||
|
- `chore`: build, supporting tools, etc.
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
feat(task): add a task priority setting feature
|
||||||
|
|
||||||
|
Allowed users to set a priority (high/medium/low) on tasks.
|
||||||
|
- Added a priority field to the Task model
|
||||||
|
- Added a --priority option to the CLI
|
||||||
|
- Implemented sorting by priority
|
||||||
|
|
||||||
|
Closes #123
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pull Request Process
|
||||||
|
|
||||||
|
**Checks before creating**:
|
||||||
|
- [ ] All tests pass
|
||||||
|
- [ ] No lint errors
|
||||||
|
- [ ] Type checking passes
|
||||||
|
- [ ] Conflicts are resolved
|
||||||
|
|
||||||
|
**PR template**:
|
||||||
|
```markdown
|
||||||
|
## Overview
|
||||||
|
[A brief description of the changes]
|
||||||
|
|
||||||
|
## Reason for Change
|
||||||
|
[Why this change is necessary]
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- [change 1]
|
||||||
|
- [change 2]
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- [ ] Unit tests added
|
||||||
|
- [ ] Manual testing performed
|
||||||
|
|
||||||
|
## Screenshots (if applicable)
|
||||||
|
[image]
|
||||||
|
|
||||||
|
## Related Issue
|
||||||
|
Closes #[issue number]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Review process**:
|
||||||
|
1. Self-review
|
||||||
|
2. Run automated tests
|
||||||
|
3. Assign reviewers
|
||||||
|
4. Address review feedback
|
||||||
|
5. Merge after approval
|
||||||
|
|
||||||
|
## Test Strategy
|
||||||
|
|
||||||
|
### Test Types
|
||||||
|
|
||||||
|
#### Unit Tests
|
||||||
|
|
||||||
|
**Target**: individual functions and classes
|
||||||
|
|
||||||
|
**Coverage target**: [80/90/100]%
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
describe('TaskService', () => {
|
||||||
|
describe('create', () => {
|
||||||
|
it('can create a task with valid data', async () => {
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
const task = await service.create({
|
||||||
|
title: 'Test task',
|
||||||
|
description: 'Description',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(task.id).toBeDefined();
|
||||||
|
expect(task.title).toBe('Test task');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws ValidationError when the title is empty', async () => {
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.create({ title: '' })
|
||||||
|
).rejects.toThrow(ValidationError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Integration Tests
|
||||||
|
|
||||||
|
**Target**: coordination of multiple components
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
describe('Task CRUD', () => {
|
||||||
|
it('can create, read, update, and delete a task', async () => {
|
||||||
|
// Create
|
||||||
|
const created = await taskService.create({ title: 'Test' });
|
||||||
|
|
||||||
|
// Read
|
||||||
|
const found = await taskService.findById(created.id);
|
||||||
|
expect(found?.title).toBe('Test');
|
||||||
|
|
||||||
|
// Update
|
||||||
|
await taskService.update(created.id, { title: 'Updated' });
|
||||||
|
const updated = await taskService.findById(created.id);
|
||||||
|
expect(updated?.title).toBe('Updated');
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
await taskService.delete(created.id);
|
||||||
|
const deleted = await taskService.findById(created.id);
|
||||||
|
expect(deleted).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### E2E Tests
|
||||||
|
|
||||||
|
**Target**: the entire user scenario
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
describe('Task management flow', () => {
|
||||||
|
it('a user can add and complete a task', async () => {
|
||||||
|
// Add a task
|
||||||
|
await cli.run(['add', 'New task']);
|
||||||
|
expect(output).toContain('Task added');
|
||||||
|
|
||||||
|
// Display the task list
|
||||||
|
await cli.run(['list']);
|
||||||
|
expect(output).toContain('New task');
|
||||||
|
|
||||||
|
// Complete the task
|
||||||
|
await cli.run(['complete', '1']);
|
||||||
|
expect(output).toContain('Task completed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Naming Conventions
|
||||||
|
|
||||||
|
**Pattern**: `[target]_[condition]_[expectedResult]`
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example
|
||||||
|
it('create_emptyTitle_throwsValidationError', () => { });
|
||||||
|
it('findById_existingId_returnsTask', () => { });
|
||||||
|
it('delete_nonExistentId_throwsNotFoundError', () => { });
|
||||||
|
|
||||||
|
// ❌ Bad example
|
||||||
|
it('test1', () => { });
|
||||||
|
it('works', () => { });
|
||||||
|
it('should work correctly', () => { });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Mocks and Stubs
|
||||||
|
|
||||||
|
**Principles**:
|
||||||
|
- Mock external dependencies (API, DB, file system)
|
||||||
|
- Use the real implementation for business logic
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
// Mock the repository
|
||||||
|
const mockRepository: ITaskRepository = {
|
||||||
|
save: jest.fn(),
|
||||||
|
findById: jest.fn(),
|
||||||
|
findAll: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// The service uses the real implementation
|
||||||
|
const service = new TaskService(mockRepository);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Review Criteria
|
||||||
|
|
||||||
|
### Review Points
|
||||||
|
|
||||||
|
**Functionality**:
|
||||||
|
- [ ] Does it meet the requirements?
|
||||||
|
- [ ] Are edge cases considered?
|
||||||
|
- [ ] Is error handling appropriate?
|
||||||
|
|
||||||
|
**Readability**:
|
||||||
|
- [ ] Is the naming clear?
|
||||||
|
- [ ] Are the comments appropriate?
|
||||||
|
- [ ] Is complex logic explained?
|
||||||
|
|
||||||
|
**Maintainability**:
|
||||||
|
- [ ] Is there any duplicate code?
|
||||||
|
- [ ] Are responsibilities clearly separated?
|
||||||
|
- [ ] Is the impact scope of changes limited?
|
||||||
|
|
||||||
|
**Performance**:
|
||||||
|
- [ ] Are there any unnecessary computations?
|
||||||
|
- [ ] Is there any possibility of a memory leak?
|
||||||
|
- [ ] Are database queries optimized?
|
||||||
|
|
||||||
|
**Security**:
|
||||||
|
- [ ] Is input validation appropriate?
|
||||||
|
- [ ] Is any sensitive information hardcoded?
|
||||||
|
- [ ] Are permission checks implemented?
|
||||||
|
|
||||||
|
### How to Write Review Comments
|
||||||
|
|
||||||
|
**Constructive feedback**:
|
||||||
|
```markdown
|
||||||
|
## ✅ Good example
|
||||||
|
With this implementation, performance may degrade as the number of tasks grows.
|
||||||
|
How about considering a search that uses an index instead?
|
||||||
|
|
||||||
|
## ❌ Bad example
|
||||||
|
This way of writing it is not good.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Indicate priority**:
|
||||||
|
- `[required]`: must be fixed
|
||||||
|
- `[recommended]`: fix recommended
|
||||||
|
- `[suggestion]`: please consider
|
||||||
|
- `[question]`: a question for understanding
|
||||||
|
|
||||||
|
## Development Environment Setup
|
||||||
|
|
||||||
|
### Required Tools
|
||||||
|
|
||||||
|
| Tool | Version | Installation method |
|
||||||
|
|--------|-----------|-----------------|
|
||||||
|
| [tool 1] | [version] | [command] |
|
||||||
|
| [tool 2] | [version] | [command] |
|
||||||
|
|
||||||
|
### Setup Procedure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Clone the repository
|
||||||
|
git clone [URL]
|
||||||
|
cd [project-name]
|
||||||
|
|
||||||
|
# 2. Install dependencies
|
||||||
|
[install command]
|
||||||
|
|
||||||
|
# 3. Set environment variables
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit the .env file
|
||||||
|
|
||||||
|
# 4. Start the development server
|
||||||
|
[start command]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recommended Development Tools (if applicable)
|
||||||
|
|
||||||
|
- [tool 1]: [description]
|
||||||
|
- [tool 2]: [description]
|
||||||
52
.opencode/skills/functional-design/SKILL.md
Normal file
52
.opencode/skills/functional-design/SKILL.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
name: functional-design
|
||||||
|
description: Detailed guide and template for creating a functional design document. Use only when creating a functional design document.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Functional Design Document Creation Skill
|
||||||
|
|
||||||
|
This skill is a detailed guide for creating high-quality functional design documents.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before starting to create a functional design document, confirm the following:
|
||||||
|
|
||||||
|
### docs/product-requirements.md has been created
|
||||||
|
|
||||||
|
**Required**: The PRD must exist at the following location:
|
||||||
|
|
||||||
|
**File path**: `docs/product-requirements.md`
|
||||||
|
|
||||||
|
The functional design document details how to technically realize the requirements defined in the PRD.
|
||||||
|
|
||||||
|
## Priority of Existing Documents
|
||||||
|
|
||||||
|
**Important**: If an existing functional design document is present at `docs/functional-design.md`,
|
||||||
|
follow the priority order below:
|
||||||
|
|
||||||
|
1. **Existing functional design document (`docs/functional-design.md`)** - Highest priority
|
||||||
|
- Contains project-specific design
|
||||||
|
- Takes precedence over this skill's guide
|
||||||
|
|
||||||
|
2. **This skill's guide** - Reference material
|
||||||
|
- Generic templates and examples
|
||||||
|
- Use when no existing design document exists, or as a supplement
|
||||||
|
|
||||||
|
**When creating new**: Refer to this skill's template and guide
|
||||||
|
**When updating**: Update while maintaining the structure and content of the existing design document
|
||||||
|
|
||||||
|
## Output Destination
|
||||||
|
|
||||||
|
Save the created functional design document to the following location:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/functional-design.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template Reference
|
||||||
|
|
||||||
|
When creating a functional design document, use the following template: ./template.md
|
||||||
|
|
||||||
|
## Detailed Guide
|
||||||
|
|
||||||
|
For a more detailed creation guide, refer to the following file: ./guide.md
|
||||||
470
.opencode/skills/functional-design/guide.md
Normal file
470
.opencode/skills/functional-design/guide.md
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
# Functional Design Document Creation Guide
|
||||||
|
|
||||||
|
This guide provides practical guidance for creating a functional design document based on a Product Requirements Document (PRD).
|
||||||
|
|
||||||
|
## Purpose of the Functional Design Document
|
||||||
|
|
||||||
|
The functional design document translates the "what to build" defined in the PRD into "how to realize it".
|
||||||
|
|
||||||
|
**Main Content**:
|
||||||
|
- System architecture diagram
|
||||||
|
- Data model
|
||||||
|
- Component design
|
||||||
|
- Algorithm design (if applicable)
|
||||||
|
- UI design
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
## Basic Creation Flow
|
||||||
|
|
||||||
|
### Step 1: Review the PRD
|
||||||
|
|
||||||
|
Before creating the functional design document, always review the PRD.
|
||||||
|
|
||||||
|
```
|
||||||
|
Example prompt for having opencode create a functional design document from the PRD:
|
||||||
|
|
||||||
|
Create a functional design document based on the content of the PRD.
|
||||||
|
In particular, focus on the priority P0 (MVP) features.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Create the System Architecture Diagram
|
||||||
|
|
||||||
|
#### Using Mermaid Notation
|
||||||
|
|
||||||
|
Write the system architecture diagram using Mermaid notation.
|
||||||
|
|
||||||
|
**Example of a basic 3-tier architecture**:
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
User[User]
|
||||||
|
CLI[CLI Layer]
|
||||||
|
Service[Service Layer]
|
||||||
|
Data[Data Layer]
|
||||||
|
|
||||||
|
User --> CLI
|
||||||
|
CLI --> Service
|
||||||
|
Service --> Data
|
||||||
|
```
|
||||||
|
|
||||||
|
**A more detailed example**:
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
User[User]
|
||||||
|
CLI[CLI Interface]
|
||||||
|
Commander[Commander.js]
|
||||||
|
TaskManager[TaskManager]
|
||||||
|
PriorityEstimator[PriorityEstimator]
|
||||||
|
FileStorage[FileStorage]
|
||||||
|
JSON[(tasks.json)]
|
||||||
|
|
||||||
|
User --> CLI
|
||||||
|
CLI --> Commander
|
||||||
|
Commander --> TaskManager
|
||||||
|
TaskManager --> PriorityEstimator
|
||||||
|
TaskManager --> FileStorage
|
||||||
|
FileStorage --> JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Define the Data Model
|
||||||
|
|
||||||
|
#### Define Clearly with TypeScript Type Definitions
|
||||||
|
|
||||||
|
Define the data model using TypeScript interfaces.
|
||||||
|
|
||||||
|
**Example of a basic Task type**:
|
||||||
|
```typescript
|
||||||
|
interface Task {
|
||||||
|
id: string; // UUID v4
|
||||||
|
title: string; // 1-200 characters
|
||||||
|
description?: string; // Optional, Markdown format
|
||||||
|
status: TaskStatus; // 'todo' | 'in_progress' | 'completed'
|
||||||
|
priority: TaskPriority; // 'high' | 'medium' | 'low'
|
||||||
|
estimatedPriority?: TaskPriority; // Automatically estimated priority
|
||||||
|
dueDate?: Date; // Due date
|
||||||
|
createdAt: Date; // Creation timestamp
|
||||||
|
updatedAt: Date; // Update timestamp
|
||||||
|
statusHistory?: StatusChange[]; // Status change history
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskStatus = 'todo' | 'in_progress' | 'completed';
|
||||||
|
type TaskPriority = 'high' | 'medium' | 'low';
|
||||||
|
|
||||||
|
interface StatusChange {
|
||||||
|
from: TaskStatus;
|
||||||
|
to: TaskStatus;
|
||||||
|
changedAt: Date;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Points**:
|
||||||
|
- Add a comment explaining each field
|
||||||
|
- Clearly note constraints (character count, format, etc.)
|
||||||
|
- Add `?` to optional fields
|
||||||
|
- Improve readability with type aliases
|
||||||
|
|
||||||
|
#### Creating an ER Diagram
|
||||||
|
|
||||||
|
When there are multiple entities, show the relationships with an ER diagram.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
TASK ||--o{ SUBTASK : has
|
||||||
|
TASK ||--o{ TAG : has
|
||||||
|
USER ||--o{ TASK : creates
|
||||||
|
|
||||||
|
TASK {
|
||||||
|
string id PK
|
||||||
|
string title
|
||||||
|
string status
|
||||||
|
datetime createdAt
|
||||||
|
}
|
||||||
|
SUBTASK {
|
||||||
|
string id PK
|
||||||
|
string taskId FK
|
||||||
|
string title
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Component Design
|
||||||
|
|
||||||
|
Clarify the responsibilities of each layer.
|
||||||
|
|
||||||
|
#### CLI Layer
|
||||||
|
|
||||||
|
**Responsibility**: Accept user input, validation, display results
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// CommandLineInterface
|
||||||
|
class CLI {
|
||||||
|
// Accept user input
|
||||||
|
parseArguments(): Command;
|
||||||
|
|
||||||
|
// Display results
|
||||||
|
displayResult(result: Result): void;
|
||||||
|
|
||||||
|
// Display errors
|
||||||
|
displayError(error: Error): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Service Layer
|
||||||
|
|
||||||
|
**Responsibility**: Implement business logic
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// TaskManager
|
||||||
|
class TaskManager {
|
||||||
|
// Create a task
|
||||||
|
createTask(data: CreateTaskData): Task;
|
||||||
|
|
||||||
|
// Get the task list
|
||||||
|
listTasks(filter?: FilterOptions): Task[];
|
||||||
|
|
||||||
|
// Update a task
|
||||||
|
updateTask(id: string, data: UpdateTaskData): Task;
|
||||||
|
|
||||||
|
// Delete a task
|
||||||
|
deleteTask(id: string): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Data Layer
|
||||||
|
|
||||||
|
**Responsibility**: Data persistence and retrieval
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// FileStorage
|
||||||
|
class FileStorage {
|
||||||
|
// Save data
|
||||||
|
save(data: any): void;
|
||||||
|
|
||||||
|
// Load data
|
||||||
|
load(): any;
|
||||||
|
|
||||||
|
// Check whether the file exists
|
||||||
|
exists(): boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Algorithm Design (if applicable)
|
||||||
|
|
||||||
|
Design complex logic (e.g., automatic priority estimation) in detail.
|
||||||
|
|
||||||
|
#### Example of an Automatic Priority Estimation Algorithm
|
||||||
|
|
||||||
|
**Purpose**: Automatically estimate priority from a task's due date, creation timestamp, and status
|
||||||
|
|
||||||
|
**Calculation Logic**:
|
||||||
|
|
||||||
|
##### Step 1: Deadline Score Calculation (0-100 points)
|
||||||
|
```
|
||||||
|
- Overdue: 100 points (highest)
|
||||||
|
- 0-3 days until due: 90 points
|
||||||
|
- 4-7 days until due: 70 points
|
||||||
|
- 8-14 days until due: 50 points
|
||||||
|
- 14 or more days until due: 30 points
|
||||||
|
- No due date set: 20 points
|
||||||
|
```
|
||||||
|
|
||||||
|
**Formula**:
|
||||||
|
```typescript
|
||||||
|
function calculateDeadlineScore(dueDate?: Date): number {
|
||||||
|
if (!dueDate) return 20;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const daysRemaining = Math.floor((dueDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (daysRemaining < 0) return 100; // Overdue
|
||||||
|
if (daysRemaining <= 3) return 90;
|
||||||
|
if (daysRemaining <= 7) return 70;
|
||||||
|
if (daysRemaining <= 14) return 50;
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Step 2: Elapsed Time Score Calculation (0-100 points)
|
||||||
|
```
|
||||||
|
- 30 or more days since creation: 100 points (highest)
|
||||||
|
- 21-30 days since creation: 80 points
|
||||||
|
- 14-21 days since creation: 60 points
|
||||||
|
- 7-14 days since creation: 40 points
|
||||||
|
- Less than 7 days since creation: 20 points
|
||||||
|
```
|
||||||
|
|
||||||
|
**Formula**:
|
||||||
|
```typescript
|
||||||
|
function calculateAgeScore(createdAt: Date): number {
|
||||||
|
const now = new Date();
|
||||||
|
const daysOld = Math.floor((now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (daysOld >= 30) return 100;
|
||||||
|
if (daysOld >= 21) return 80;
|
||||||
|
if (daysOld >= 14) return 60;
|
||||||
|
if (daysOld >= 7) return 40;
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Step 3: Status Score Calculation (0-100 points)
|
||||||
|
```
|
||||||
|
- In progress (in_progress): 100 points (highest priority)
|
||||||
|
- Not started (todo): 50 points
|
||||||
|
- Completed (completed): 0 points
|
||||||
|
```
|
||||||
|
|
||||||
|
**Formula**:
|
||||||
|
```typescript
|
||||||
|
function calculateStatusScore(status: TaskStatus): number {
|
||||||
|
if (status === 'in_progress') return 100;
|
||||||
|
if (status === 'todo') return 50;
|
||||||
|
return 0; // completed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Step 4: Total Score Calculation
|
||||||
|
|
||||||
|
**Weighted Average**:
|
||||||
|
```
|
||||||
|
Total Score = (Deadline Score × 50%) + (Elapsed Time Score × 20%) + (Status Score × 30%)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Formula**:
|
||||||
|
```typescript
|
||||||
|
function calculateTotalScore(task: Task): number {
|
||||||
|
const deadlineScore = calculateDeadlineScore(task.dueDate);
|
||||||
|
const ageScore = calculateAgeScore(task.createdAt);
|
||||||
|
const statusScore = calculateStatusScore(task.status);
|
||||||
|
|
||||||
|
return (deadlineScore * 0.5) + (ageScore * 0.2) + (statusScore * 0.3);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Step 5: Priority Classification
|
||||||
|
|
||||||
|
**Classification by Threshold**:
|
||||||
|
```
|
||||||
|
- 70 points or more: high (high priority)
|
||||||
|
- 40-70 points: medium (medium priority)
|
||||||
|
- Less than 40 points: low (low priority)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Formula**:
|
||||||
|
```typescript
|
||||||
|
function estimatePriority(task: Task): TaskPriority {
|
||||||
|
const score = calculateTotalScore(task);
|
||||||
|
|
||||||
|
if (score >= 70) return 'high';
|
||||||
|
if (score >= 40) return 'medium';
|
||||||
|
return 'low';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Complete Implementation Example**:
|
||||||
|
```typescript
|
||||||
|
class PriorityEstimator {
|
||||||
|
estimate(task: Task): TaskPriority {
|
||||||
|
const deadlineScore = this.calculateDeadlineScore(task.dueDate);
|
||||||
|
const ageScore = this.calculateAgeScore(task.createdAt);
|
||||||
|
const statusScore = this.calculateStatusScore(task.status);
|
||||||
|
|
||||||
|
const totalScore = (deadlineScore * 0.5) + (ageScore * 0.2) + (statusScore * 0.3);
|
||||||
|
|
||||||
|
if (totalScore >= 70) return 'high';
|
||||||
|
if (totalScore >= 40) return 'medium';
|
||||||
|
return 'low';
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateDeadlineScore(dueDate?: Date): number {
|
||||||
|
if (!dueDate) return 20;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const daysRemaining = Math.floor((dueDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (daysRemaining < 0) return 100;
|
||||||
|
if (daysRemaining <= 3) return 90;
|
||||||
|
if (daysRemaining <= 7) return 70;
|
||||||
|
if (daysRemaining <= 14) return 50;
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateAgeScore(createdAt: Date): number {
|
||||||
|
const now = new Date();
|
||||||
|
const daysOld = Math.floor((now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (daysOld >= 30) return 100;
|
||||||
|
if (daysOld >= 21) return 80;
|
||||||
|
if (daysOld >= 14) return 60;
|
||||||
|
if (daysOld >= 7) return 40;
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateStatusScore(status: TaskStatus): number {
|
||||||
|
if (status === 'in_progress') return 100;
|
||||||
|
if (status === 'todo') return 50;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Use Case Diagram
|
||||||
|
|
||||||
|
Express the main use cases with sequence diagrams.
|
||||||
|
|
||||||
|
**Task Addition Flow**:
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant CLI
|
||||||
|
participant TaskManager
|
||||||
|
participant PriorityEstimator
|
||||||
|
participant FileStorage
|
||||||
|
|
||||||
|
User->>CLI: devtask add "task"
|
||||||
|
CLI->>CLI: Validate input
|
||||||
|
CLI->>TaskManager: createTask(data)
|
||||||
|
TaskManager->>TaskManager: Create task object
|
||||||
|
TaskManager->>PriorityEstimator: estimate(task)
|
||||||
|
PriorityEstimator-->>TaskManager: Estimated priority
|
||||||
|
TaskManager->>FileStorage: save(task)
|
||||||
|
FileStorage-->>TaskManager: Success
|
||||||
|
TaskManager-->>CLI: Created task
|
||||||
|
CLI-->>User: "Task created (ID: xxx)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7: UI Design (if applicable)
|
||||||
|
|
||||||
|
For CLI tools, define table display and color coding.
|
||||||
|
|
||||||
|
#### Table Display
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────┬──────────────────┬────────────┬──────────┬───────────────┐
|
||||||
|
│ ID │ Title │ Status │ Priority │ Due Date │
|
||||||
|
├──────────┼──────────────────┼────────────┼──────────┼───────────────┤
|
||||||
|
│ 7a5c6ff0 │ Buy milk on the │ Not started│ High │ 2025-11-05 │
|
||||||
|
│ │ way home. │ │ │ (1 day left) │
|
||||||
|
└──────────┴──────────────────┴────────────┴──────────┴───────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Color Coding
|
||||||
|
|
||||||
|
**Status Color Coding**:
|
||||||
|
- Completed (completed): green
|
||||||
|
- In progress (in_progress): yellow
|
||||||
|
- Not started (todo): white
|
||||||
|
|
||||||
|
**Priority Color Coding**:
|
||||||
|
- High (high): red
|
||||||
|
- Medium (medium): yellow
|
||||||
|
- Low (low): blue
|
||||||
|
|
||||||
|
### Step 8: File Structure (if applicable)
|
||||||
|
|
||||||
|
Define the data storage format.
|
||||||
|
|
||||||
|
**Example: Data storage for a CLI tool**:
|
||||||
|
```
|
||||||
|
.devtask/
|
||||||
|
├── tasks.json # Task data
|
||||||
|
└── config.json # Configuration data
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example of tasks.json**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"id": "7a5c6ff0-5f55-474e-baf7-ea13624d73a4",
|
||||||
|
"title": "Buy milk on the way home",
|
||||||
|
"description": "",
|
||||||
|
"status": "todo",
|
||||||
|
"priority": "high",
|
||||||
|
"estimatedPriority": "medium",
|
||||||
|
"dueDate": "2025-11-05T00:00:00.000Z",
|
||||||
|
"createdAt": "2025-11-04T10:00:00.000Z",
|
||||||
|
"updatedAt": "2025-11-04T10:00:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 9: Error Handling
|
||||||
|
|
||||||
|
Define the types of errors and how to handle them.
|
||||||
|
|
||||||
|
| Error Category | Handling | Display to User |
|
||||||
|
|-----------|------|-----------------|
|
||||||
|
| Input validation error | Abort processing, display error message | "Please enter a title of 1-200 characters" |
|
||||||
|
| File read error | Continue with empty initial data | "Data file not found. Creating a new one" |
|
||||||
|
| Task not found | Abort processing, display error message | "Task not found (ID: xxx)" |
|
||||||
|
|
||||||
|
## Reviewing the Functional Design Document
|
||||||
|
|
||||||
|
### Review Perspectives
|
||||||
|
|
||||||
|
Request a review from opencode:
|
||||||
|
|
||||||
|
```
|
||||||
|
Please evaluate this functional design document. Check it from the following perspectives:
|
||||||
|
|
||||||
|
1. Does it satisfy the requirements of the PRD?
|
||||||
|
2. Is the data model concrete?
|
||||||
|
3. Are the responsibilities of the components clear?
|
||||||
|
4. Is the algorithm detailed to an implementable level?
|
||||||
|
5. Is error handling comprehensive?
|
||||||
|
```
|
||||||
|
|
||||||
|
### Implementing Improvements
|
||||||
|
|
||||||
|
Make improvements based on opencode's feedback.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Keys to successfully creating a functional design document:
|
||||||
|
|
||||||
|
1. **Consistency with the PRD**: Accurately reflect the requirements defined in the PRD
|
||||||
|
2. **Leverage Mermaid notation**: Express things visually with diagrams
|
||||||
|
3. **TypeScript type definitions**: Make the data model clear
|
||||||
|
4. **Detailed algorithm design**: Be concrete about complex logic
|
||||||
|
5. **Layer separation**: Clarify the responsibilities of each component
|
||||||
|
6. **Implementable level**: A level of detail that lets developers implement without confusion
|
||||||
262
.opencode/skills/functional-design/template.md
Normal file
262
.opencode/skills/functional-design/template.md
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
# Functional Design Document
|
||||||
|
|
||||||
|
## System Architecture Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
User[User]
|
||||||
|
CLI[CLI Layer]
|
||||||
|
Service[Service Layer]
|
||||||
|
Data[Data Layer]
|
||||||
|
|
||||||
|
User --> CLI
|
||||||
|
CLI --> Service
|
||||||
|
Service --> Data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
| Category | Technology | Selection Rationale |
|
||||||
|
|------|------|----------|
|
||||||
|
| Language | [Language name] | [Rationale] |
|
||||||
|
| Framework | [Name] | [Rationale] |
|
||||||
|
| Database | [Name] | [Rationale] |
|
||||||
|
| Tools | [Name] | [Rationale] |
|
||||||
|
|
||||||
|
## Data Model Definition
|
||||||
|
|
||||||
|
### Entity: [Entity Name]
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface [EntityName] {
|
||||||
|
id: string; // UUID
|
||||||
|
[field1]: [type]; // [description]
|
||||||
|
[field2]: [type]; // [description]
|
||||||
|
createdAt: Date; // Creation timestamp
|
||||||
|
updatedAt: Date; // Update timestamp
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Constraints**:
|
||||||
|
- [Constraint 1]
|
||||||
|
- [Constraint 2]
|
||||||
|
|
||||||
|
### ER Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
ENTITY1 ||--o{ ENTITY2 : has
|
||||||
|
ENTITY1 {
|
||||||
|
string id PK
|
||||||
|
string field1
|
||||||
|
datetime createdAt
|
||||||
|
}
|
||||||
|
ENTITY2 {
|
||||||
|
string id PK
|
||||||
|
string entity1Id FK
|
||||||
|
string field1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Design
|
||||||
|
|
||||||
|
### [Component 1]
|
||||||
|
|
||||||
|
**Responsibilities**:
|
||||||
|
- [Responsibility 1]
|
||||||
|
- [Responsibility 2]
|
||||||
|
|
||||||
|
**Interface**:
|
||||||
|
```typescript
|
||||||
|
class [ComponentName] {
|
||||||
|
[method1]([params]): [return];
|
||||||
|
[method2]([params]): [return];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- [Dependency 1]
|
||||||
|
- [Dependency 2]
|
||||||
|
|
||||||
|
## Use Case Diagram
|
||||||
|
|
||||||
|
### [Use Case 1]
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant CLI
|
||||||
|
participant Service
|
||||||
|
participant Data
|
||||||
|
|
||||||
|
User->>CLI: [action1]
|
||||||
|
CLI->>Service: [action2]
|
||||||
|
Service->>Data: [action3]
|
||||||
|
Data-->>Service: [response]
|
||||||
|
Service-->>CLI: [response]
|
||||||
|
CLI-->>User: [result]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flow Description**:
|
||||||
|
1. [Step 1]
|
||||||
|
2. [Step 2]
|
||||||
|
3. [Step 3]
|
||||||
|
|
||||||
|
## Screen Transition Diagram (if applicable)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> State1
|
||||||
|
State1 --> State2: [action]
|
||||||
|
State2 --> State3: [action]
|
||||||
|
State3 --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Design (if applicable)
|
||||||
|
|
||||||
|
### [Endpoint 1]
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/[resource]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"[field]": "[value]"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"[field]": "[value]"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error Responses**:
|
||||||
|
- 400 Bad Request: [Condition]
|
||||||
|
- 404 Not Found: [Condition]
|
||||||
|
- 500 Internal Server Error: [Condition]
|
||||||
|
|
||||||
|
## Algorithm Design (if applicable)
|
||||||
|
|
||||||
|
### [Algorithm Name]
|
||||||
|
|
||||||
|
**Purpose**: [Description]
|
||||||
|
|
||||||
|
**Calculation Logic**:
|
||||||
|
|
||||||
|
#### Step 1: [Step Name]
|
||||||
|
- [Detailed description]
|
||||||
|
- Formula: `[formula]`
|
||||||
|
- Score range: 0-100 points
|
||||||
|
|
||||||
|
#### Step 2: [Step Name]
|
||||||
|
- [Detailed description]
|
||||||
|
- Formula: `[formula]`
|
||||||
|
- Score range: 0-100 points
|
||||||
|
|
||||||
|
#### Step 3: Total Score Calculation
|
||||||
|
- Weighted average: `Total Score = (Step 1 × Weight 1) + (Step 2 × Weight 2)`
|
||||||
|
- Weight allocation:
|
||||||
|
- Step 1: [%]
|
||||||
|
- Step 2: [%]
|
||||||
|
|
||||||
|
#### Step 4: Classification
|
||||||
|
- [Classification 1]: Score >= [threshold]
|
||||||
|
- [Classification 2]: [threshold] <= Score < [threshold]
|
||||||
|
- [Classification 3]: Score < [threshold]
|
||||||
|
|
||||||
|
**Implementation Example**:
|
||||||
|
```typescript
|
||||||
|
function [algorithmName]([params]): [return] {
|
||||||
|
// Step 1
|
||||||
|
const score1 = [calculation];
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
const score2 = [calculation];
|
||||||
|
|
||||||
|
// Total score
|
||||||
|
const totalScore = (score1 * weight1) + (score2 * weight2);
|
||||||
|
|
||||||
|
// Classification
|
||||||
|
if (totalScore >= threshold1) return '[classification1]';
|
||||||
|
if (totalScore >= threshold2) return '[classification2]';
|
||||||
|
return '[classification3]';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## UI Design (if applicable)
|
||||||
|
|
||||||
|
### Table Display
|
||||||
|
|
||||||
|
**Display Items**:
|
||||||
|
| Item | Description | Format |
|
||||||
|
|------|------|-------------|
|
||||||
|
| [Item 1] | [Description] | [Format] |
|
||||||
|
| [Item 2] | [Description] | [Format] |
|
||||||
|
|
||||||
|
### Color Coding
|
||||||
|
|
||||||
|
**Color Usage**:
|
||||||
|
- [Color 1]: [Use] (e.g., green = completed)
|
||||||
|
- [Color 2]: [Use] (e.g., yellow = in progress)
|
||||||
|
- [Color 3]: [Use] (e.g., red = not started)
|
||||||
|
|
||||||
|
### Interactive Mode (if applicable)
|
||||||
|
|
||||||
|
**Operation Flow**:
|
||||||
|
1. [Operation 1]
|
||||||
|
2. [Operation 2]
|
||||||
|
3. [Operation 3]
|
||||||
|
|
||||||
|
## File Structure (if applicable)
|
||||||
|
|
||||||
|
**Data Storage Format**:
|
||||||
|
```
|
||||||
|
[directory]/
|
||||||
|
├── [file1].json # [description]
|
||||||
|
└── [file2].json # [description]
|
||||||
|
```
|
||||||
|
|
||||||
|
**File Content Example**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"[field]": "[value]"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Optimization
|
||||||
|
|
||||||
|
- [Optimization 1]: [Description]
|
||||||
|
- [Optimization 2]: [Description]
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- [Consideration 1]: [Countermeasure]
|
||||||
|
- [Consideration 2]: [Countermeasure]
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Error Classification
|
||||||
|
|
||||||
|
| Error Category | Handling | Display to User |
|
||||||
|
|-----------|------|-----------------|
|
||||||
|
| [Category 1] | [Handling details] | [Message] |
|
||||||
|
| [Category 2] | [Handling details] | [Message] |
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- [Target 1]
|
||||||
|
- [Target 2]
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- [Scenario 1]
|
||||||
|
- [Scenario 2]
|
||||||
|
|
||||||
|
### E2E Tests
|
||||||
|
- [Scenario 1]
|
||||||
|
- [Scenario 2]
|
||||||
55
.opencode/skills/glossary-creation/SKILL.md
Normal file
55
.opencode/skills/glossary-creation/SKILL.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
name: glossary-creation
|
||||||
|
description: Detailed guide and template for creating a glossary. Use only when creating a glossary.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Glossary Creation Skill
|
||||||
|
|
||||||
|
This skill is a detailed guide for systematically defining project-specific terms and technical terms.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before starting to create a glossary, confirm the following:
|
||||||
|
|
||||||
|
### Recommended Documents
|
||||||
|
|
||||||
|
1. **docs/product-requirements.md** (PRD)
|
||||||
|
2. **docs/functional-design.md** (Functional Design Document)
|
||||||
|
3. **docs/architecture.md** (Architecture Design Document)
|
||||||
|
4. **docs/repository-structure.md** (Repository Structure)
|
||||||
|
5. **docs/development-guidelines.md** (Development Guidelines)
|
||||||
|
|
||||||
|
The glossary defines the terms used across all documents in a unified way.
|
||||||
|
Extract terms from each document and organize them systematically.
|
||||||
|
|
||||||
|
## Priority of Existing Documents
|
||||||
|
|
||||||
|
**Important**: If an existing glossary is present at `docs/glossary.md`,
|
||||||
|
follow the priority order below:
|
||||||
|
|
||||||
|
1. **Existing glossary (`docs/glossary.md`)** - Highest priority
|
||||||
|
- Contains project-specific term definitions
|
||||||
|
- Takes precedence over this skill's guide
|
||||||
|
|
||||||
|
2. **This skill's guide** - Reference material
|
||||||
|
- Generic templates and examples
|
||||||
|
- Use when no existing glossary exists, or as a supplement
|
||||||
|
|
||||||
|
**When creating new**: Refer to this skill's template and guide
|
||||||
|
**When updating**: Update while maintaining the structure and content of the existing glossary
|
||||||
|
|
||||||
|
## Output Destination
|
||||||
|
|
||||||
|
Save the created glossary to the following location:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/glossary.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template Reference
|
||||||
|
|
||||||
|
When creating a glossary, use the following template: ./template.md
|
||||||
|
|
||||||
|
## Detailed Guide
|
||||||
|
|
||||||
|
For a more detailed creation guide, refer to the following file: ./guide.md
|
||||||
509
.opencode/skills/glossary-creation/guide.md
Normal file
509
.opencode/skills/glossary-creation/guide.md
Normal file
@ -0,0 +1,509 @@
|
|||||||
|
# Glossary Creation Guide
|
||||||
|
|
||||||
|
## Basic principles
|
||||||
|
|
||||||
|
### 1. Clear and consistent definitions
|
||||||
|
|
||||||
|
Term definitions should eliminate ambiguity so that everyone who reads them arrives at the same understanding.
|
||||||
|
|
||||||
|
**Bad example**:
|
||||||
|
```markdown
|
||||||
|
## Task
|
||||||
|
What the user should do
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good example**:
|
||||||
|
```markdown
|
||||||
|
## Task (Task)
|
||||||
|
**Definition**: A unit of work that the user must complete. It has a title, description, due date,
|
||||||
|
and status (not started / in progress / completed).
|
||||||
|
|
||||||
|
**Related terms**: Subtask, Task group
|
||||||
|
|
||||||
|
**Usage examples**:
|
||||||
|
- "Add a task": Register a new task in the system
|
||||||
|
- "Complete a task": Change the task's status to completed
|
||||||
|
|
||||||
|
**Data model**: `src/types/Task.ts`
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Include concrete examples
|
||||||
|
|
||||||
|
Show concrete usage examples, not just abstract definitions.
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## Priority (Priority)
|
||||||
|
**Definition**: A three-level indicator showing a task's importance and urgency
|
||||||
|
|
||||||
|
**Value definitions**:
|
||||||
|
- `high`: Urgent and important. Requires immediate response
|
||||||
|
- `medium`: Important but not urgent. Handle in a planned manner
|
||||||
|
- `low`: Low in both importance and urgency. Handle if there is time
|
||||||
|
|
||||||
|
**Decision criteria**:
|
||||||
|
- high: Due within 24 hours, or blocking other tasks
|
||||||
|
- medium: Due within one week
|
||||||
|
- low: Due more than one week out, or no due date
|
||||||
|
|
||||||
|
**Usage example**:
|
||||||
|
```typescript
|
||||||
|
const task: Task = {
|
||||||
|
title: 'Fix security vulnerability',
|
||||||
|
priority: 'high', // Requires urgent response
|
||||||
|
};
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Link related terms
|
||||||
|
|
||||||
|
Make the relationships between terms clear.
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## Task (Task)
|
||||||
|
**Definition**: [Definition]
|
||||||
|
|
||||||
|
**Related terms**:
|
||||||
|
- [Subtask](#subtask): A task broken down into smaller parts
|
||||||
|
- [Task group](#task-group): Multiple tasks grouped together
|
||||||
|
- [Status](#task-status): The progress state of a task
|
||||||
|
|
||||||
|
**Parent-child relationship**:
|
||||||
|
- Parent: Task group
|
||||||
|
- Child: Subtask
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to classify terms
|
||||||
|
|
||||||
|
### Defining domain terms
|
||||||
|
|
||||||
|
**Target**: Project-specific business concepts
|
||||||
|
|
||||||
|
**Items to define**:
|
||||||
|
```markdown
|
||||||
|
## [Term]
|
||||||
|
|
||||||
|
**Definition**: [A concise definition in 1-2 sentences]
|
||||||
|
|
||||||
|
**Description**: [Detailed explanation, background, constraints]
|
||||||
|
|
||||||
|
**Related terms**: [Other related terms]
|
||||||
|
|
||||||
|
**Usage examples**: [Specific usage scenarios]
|
||||||
|
|
||||||
|
**Data model**: [The relevant file path]
|
||||||
|
|
||||||
|
**English notation**: [English term] (when there is global expansion)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## Steering File (Steering File)
|
||||||
|
|
||||||
|
**Definition**: A temporary document created for short-term task management
|
||||||
|
|
||||||
|
**Description**:
|
||||||
|
A steering file is placed in the `.steering/[YYYYMMDD]-[task-name]/` directory
|
||||||
|
and is deleted 1-2 weeks after the task is completed. It contains the task specification,
|
||||||
|
implementation notes, review records, and so on.
|
||||||
|
|
||||||
|
**Related terms**:
|
||||||
|
- [Persistent document](#persistent-document): A document stored for the long term
|
||||||
|
- [Task mode](#task-mode): The development mode that uses steering files
|
||||||
|
|
||||||
|
**Usage examples**:
|
||||||
|
- "Create a steering file for adding a feature"
|
||||||
|
- "After the task is completed, delete or archive the steering file"
|
||||||
|
|
||||||
|
**Directory structure**:
|
||||||
|
```
|
||||||
|
.steering/
|
||||||
|
└── 20250101-add-priority-feature/
|
||||||
|
├── requirements.md # The requirements for this task
|
||||||
|
├── design.md # The design of the changes
|
||||||
|
└── tasklist.md # The task list
|
||||||
|
```
|
||||||
|
|
||||||
|
**English notation**: Steering File
|
||||||
|
```
|
||||||
|
|
||||||
|
### Defining technical terms
|
||||||
|
|
||||||
|
**Target**: The technologies, frameworks, and tools in use
|
||||||
|
|
||||||
|
**Items to define**:
|
||||||
|
```markdown
|
||||||
|
## [Technology name]
|
||||||
|
|
||||||
|
**Definition**: [A concise description of the technology]
|
||||||
|
|
||||||
|
**Official site**: [URL]
|
||||||
|
|
||||||
|
**Use in this project**: [How it is used]
|
||||||
|
|
||||||
|
**Version**: [Version in use]
|
||||||
|
|
||||||
|
**Reason for selection**: [Why this technology was chosen]
|
||||||
|
|
||||||
|
**Alternative technologies**: [Other options that were considered]
|
||||||
|
|
||||||
|
**Related documents**: [Links to internal documents]
|
||||||
|
|
||||||
|
**Configuration file**: [Path to the configuration file]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## TypeScript
|
||||||
|
|
||||||
|
**Definition**: A programming language that adds static typing to JavaScript
|
||||||
|
|
||||||
|
**Official site**: https://www.typescriptlang.org/
|
||||||
|
|
||||||
|
**Use in this project**:
|
||||||
|
All source code is written in TypeScript to ensure type safety.
|
||||||
|
|
||||||
|
**Version**: 5.3.x
|
||||||
|
|
||||||
|
**Reason for selection**:
|
||||||
|
- Improved maintainability in large-scale development
|
||||||
|
- Improved development efficiency through editor completion
|
||||||
|
- Error detection at compile time
|
||||||
|
|
||||||
|
**Alternative technologies**:
|
||||||
|
- JavaScript ESM: Cannot benefit from type checking
|
||||||
|
- Flow: Inferior to TypeScript in ecosystem maturity
|
||||||
|
|
||||||
|
**Related documents**:
|
||||||
|
- [Architecture Design Document](./architecture.md#technology-stack)
|
||||||
|
- [Development Guidelines](./development-guidelines.md#typescript-conventions)
|
||||||
|
|
||||||
|
**Configuration file**: `tsconfig.json`
|
||||||
|
```
|
||||||
|
|
||||||
|
### Defining abbreviations and acronyms
|
||||||
|
|
||||||
|
**Principles**:
|
||||||
|
- State the full name clearly
|
||||||
|
- On first appearance, write both the abbreviation and the full name
|
||||||
|
- Avoid project-specific abbreviations (use only common abbreviations)
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## CLI
|
||||||
|
|
||||||
|
**Full name**: Command Line Interface
|
||||||
|
|
||||||
|
**Meaning**: An interface operated from the command line
|
||||||
|
|
||||||
|
**Use in this project**:
|
||||||
|
Used as the main interface of the Devtask tool. Users operate tasks with commands like
|
||||||
|
`devtask add "task"`.
|
||||||
|
|
||||||
|
**Implementation**: `src/cli/` directory
|
||||||
|
|
||||||
|
**Alternative interface**: A GUI version is under consideration as a future extension
|
||||||
|
|
||||||
|
## TDD
|
||||||
|
|
||||||
|
**Full name**: Test-Driven Development
|
||||||
|
|
||||||
|
**Meaning**: A development method in which tests are written first and then the implementation follows
|
||||||
|
|
||||||
|
**Application in this project**:
|
||||||
|
TDD is adopted for all new feature development.
|
||||||
|
|
||||||
|
**Procedure**:
|
||||||
|
1. Write a test
|
||||||
|
2. Run the test → confirm it fails
|
||||||
|
3. Write the implementation
|
||||||
|
4. Run the test → confirm it succeeds
|
||||||
|
5. Refactor
|
||||||
|
|
||||||
|
**Reference**: [Development Guidelines](./development-guidelines.md#tdd)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Defining architecture terms
|
||||||
|
|
||||||
|
**Target**: Concepts related to system design and patterns
|
||||||
|
|
||||||
|
**Items to define**:
|
||||||
|
```markdown
|
||||||
|
## [Concept]
|
||||||
|
|
||||||
|
**Definition**: [Explanation of the architecture concept]
|
||||||
|
|
||||||
|
**Application in this project**: [The specific implementation method]
|
||||||
|
|
||||||
|
**Advantages**: [Reasons for adoption]
|
||||||
|
|
||||||
|
**Disadvantages**: [Constraints and trade-offs]
|
||||||
|
|
||||||
|
**Related components**: [Related components]
|
||||||
|
|
||||||
|
**Diagram**: [Structure diagram]
|
||||||
|
|
||||||
|
**References**: [References or URLs]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## Layered Architecture (Layered Architecture)
|
||||||
|
|
||||||
|
**Definition**: A design pattern that divides a system into multiple layers by role,
|
||||||
|
with a one-directional dependency from upper layers to lower layers
|
||||||
|
|
||||||
|
**Application in this project**:
|
||||||
|
A three-layer architecture is adopted:
|
||||||
|
|
||||||
|
```
|
||||||
|
UI layer (cli/)
|
||||||
|
↓
|
||||||
|
Service layer (services/)
|
||||||
|
↓
|
||||||
|
Data layer (repositories/)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responsibilities of each layer**:
|
||||||
|
- UI layer: Accepting and displaying user input
|
||||||
|
- Service layer: Implementing business logic
|
||||||
|
- Data layer: Persisting and retrieving data
|
||||||
|
|
||||||
|
**Advantages**:
|
||||||
|
- Improved maintainability through separation of concerns
|
||||||
|
- Easy to test (each layer can be tested independently)
|
||||||
|
- A limited scope of impact from changes
|
||||||
|
|
||||||
|
**Disadvantages**:
|
||||||
|
- May be over-engineering for small-scale projects
|
||||||
|
- Overhead in data conversion between layers
|
||||||
|
|
||||||
|
**Dependency rules**:
|
||||||
|
- ✅ UI layer → Service layer
|
||||||
|
- ✅ Service layer → Data layer
|
||||||
|
- ❌ Data layer → Service layer
|
||||||
|
- ❌ Data layer → UI layer
|
||||||
|
|
||||||
|
**Implementation location**: Reflected in the structure of the `src/` directory
|
||||||
|
|
||||||
|
**References**:
|
||||||
|
- [Architecture Design Document](./architecture.md)
|
||||||
|
- [Repository Structure Definition Document](./repository-structure.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Defining state transitions
|
||||||
|
|
||||||
|
**Target**: The statuses or states of an entity
|
||||||
|
|
||||||
|
**Definition method**:
|
||||||
|
|
||||||
|
1. **Enumerate in table form**
|
||||||
|
2. **State the transition conditions clearly**
|
||||||
|
3. **Visualize with a Mermaid diagram**
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## Task Status (Task Status)
|
||||||
|
|
||||||
|
**Definition**: An enumerated type that indicates the progress state of a task
|
||||||
|
|
||||||
|
**Possible values**:
|
||||||
|
|
||||||
|
| Status | Meaning | Transition condition | Next state |
|
||||||
|
|----------|------|---------|---------|
|
||||||
|
| `todo` | Not started | Initial state when the task is created | `in_progress` |
|
||||||
|
| `in_progress` | In progress | The user starts the task | `completed`, `todo` |
|
||||||
|
| `completed` | Completed | The user completes the task | `todo` (can be reopened) |
|
||||||
|
|
||||||
|
**State transition diagram**:
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> todo: Task created
|
||||||
|
todo --> in_progress: Work started
|
||||||
|
in_progress --> completed: Completed
|
||||||
|
in_progress --> todo: Interrupted
|
||||||
|
completed --> todo: Reopened
|
||||||
|
completed --> [*]: Archived
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```typescript
|
||||||
|
// src/types/Task.ts
|
||||||
|
export type TaskStatus = 'todo' | 'in_progress' | 'completed';
|
||||||
|
|
||||||
|
// Validation of state transitions
|
||||||
|
function canTransition(
|
||||||
|
from: TaskStatus,
|
||||||
|
to: TaskStatus
|
||||||
|
): boolean {
|
||||||
|
const validTransitions: Record<TaskStatus, TaskStatus[]> = {
|
||||||
|
todo: ['in_progress'],
|
||||||
|
in_progress: ['completed', 'todo'],
|
||||||
|
completed: ['todo'],
|
||||||
|
};
|
||||||
|
return validTransitions[from].includes(to);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Business rules**:
|
||||||
|
- Direct transition from `todo` → `completed` is prohibited
|
||||||
|
- A completed task can be reopened
|
||||||
|
- An archived task cannot be changed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Defining errors and exceptions
|
||||||
|
|
||||||
|
**Target**: The error classes defined in the system
|
||||||
|
|
||||||
|
**Items to define**:
|
||||||
|
```markdown
|
||||||
|
## [Error name]
|
||||||
|
|
||||||
|
**Class name**: `[ErrorClassName]`
|
||||||
|
|
||||||
|
**Inherits from**: `Error` or `[ParentError]`
|
||||||
|
|
||||||
|
**Conditions for occurrence**: [When it occurs]
|
||||||
|
|
||||||
|
**Error message format**: [The format of the message]
|
||||||
|
|
||||||
|
**How to handle**:
|
||||||
|
- User: [What the user should do]
|
||||||
|
- Developer: [What the developer should do]
|
||||||
|
|
||||||
|
**Error code**: [If applicable]
|
||||||
|
|
||||||
|
**Log level**: [ERROR, WARN, INFO]
|
||||||
|
|
||||||
|
**Implementation location**: [File path]
|
||||||
|
|
||||||
|
**Usage example**: [Code example]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
## Validation Error (Validation Error)
|
||||||
|
|
||||||
|
**Class name**: `ValidationError`
|
||||||
|
|
||||||
|
**Inherits from**: `Error`
|
||||||
|
|
||||||
|
**Conditions for occurrence**:
|
||||||
|
Occurs when user input violates a business rule.
|
||||||
|
|
||||||
|
**Error message format**:
|
||||||
|
```
|
||||||
|
[Field name]: [Error description]
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to handle**:
|
||||||
|
- User: Correct the input according to the error message
|
||||||
|
- Developer: Confirm whether the validation logic is correct
|
||||||
|
|
||||||
|
**Error code**: `VAL-XXX` (XXX is a 3-digit number)
|
||||||
|
|
||||||
|
**Log level**: WARN (because it is a user-caused error)
|
||||||
|
|
||||||
|
**Implementation location**: `src/errors/ValidationError.ts`
|
||||||
|
|
||||||
|
**Usage example**:
|
||||||
|
```typescript
|
||||||
|
// Throwing the error
|
||||||
|
if (title.length === 0) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'Title is required',
|
||||||
|
'title',
|
||||||
|
title
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling the error
|
||||||
|
try {
|
||||||
|
await taskService.create(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
console.error(`Input error: ${error.message}`);
|
||||||
|
console.error(`Field: ${error.field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Related validations**:
|
||||||
|
- Title: 1-200 characters
|
||||||
|
- Due date: Now or later
|
||||||
|
- Priority: One of high, medium, low
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintaining and updating terms
|
||||||
|
|
||||||
|
### When to add a term
|
||||||
|
|
||||||
|
**When you should add**:
|
||||||
|
- A new concept has been introduced
|
||||||
|
- A term that team members have asked about
|
||||||
|
- A term that appears three or more times in the documentation
|
||||||
|
- When an external service or API has been integrated
|
||||||
|
|
||||||
|
**When you do not need to add**:
|
||||||
|
- General programming terms (variables, functions, etc.)
|
||||||
|
- Temporary terms used only once
|
||||||
|
|
||||||
|
### Update workflow
|
||||||
|
|
||||||
|
1. **Add or change a term**
|
||||||
|
- Add it to the appropriate category
|
||||||
|
- Fill in all definition items
|
||||||
|
- Link related terms
|
||||||
|
|
||||||
|
2. **Review**
|
||||||
|
- Share with team members
|
||||||
|
- Confirm the validity of the definition
|
||||||
|
|
||||||
|
3. **Record the change history**
|
||||||
|
- Update the change history table of the glossary
|
||||||
|
- Note it in the commit message
|
||||||
|
|
||||||
|
4. **Confirm the scope of impact**
|
||||||
|
- Search for places where the term is used
|
||||||
|
- Update documents as needed
|
||||||
|
|
||||||
|
### Managing the index
|
||||||
|
|
||||||
|
**Organize in Japanese syllabary order / alphabetical order**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Index
|
||||||
|
|
||||||
|
### A-row
|
||||||
|
- [Archive](#archive) - Process term
|
||||||
|
- [Error handling](#error-handling) - Technical term
|
||||||
|
|
||||||
|
### KA-row
|
||||||
|
- [Coverage](#coverage) - Technical term
|
||||||
|
|
||||||
|
### SA-row
|
||||||
|
- [Steering file](#steering-file) - Domain term
|
||||||
|
- [Status](#task-status) - Data model term
|
||||||
|
|
||||||
|
### TA-row
|
||||||
|
- [Task](#task) - Domain term
|
||||||
|
- [TDD](#tdd) - Abbreviation
|
||||||
|
|
||||||
|
### A-Z
|
||||||
|
- [CLI](#cli) - Abbreviation
|
||||||
|
- [TypeScript](#typescript) - Technical term
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Every term is clearly defined
|
||||||
|
- [ ] Concrete examples are included
|
||||||
|
- [ ] Related terms are linked
|
||||||
|
- [ ] Categories are appropriately classified
|
||||||
|
- [ ] Technical terms include version information
|
||||||
|
- [ ] Abbreviations include their full names
|
||||||
|
- [ ] State transitions are illustrated with diagrams
|
||||||
|
- [ ] Errors include how to handle them
|
||||||
|
- [ ] The index is organized
|
||||||
|
- [ ] The change history is recorded
|
||||||
179
.opencode/skills/glossary-creation/template.md
Normal file
179
.opencode/skills/glossary-creation/template.md
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# Project Glossary
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document manages the definitions of terms used within the project.
|
||||||
|
|
||||||
|
**Updated**: [YYYY-MM-DD]
|
||||||
|
|
||||||
|
## Domain Terms
|
||||||
|
|
||||||
|
Terms related to project-specific business concepts and features.
|
||||||
|
|
||||||
|
### [Term 1]
|
||||||
|
|
||||||
|
**Definition**: [Clear definition]
|
||||||
|
|
||||||
|
**Description**: [Detailed description]
|
||||||
|
|
||||||
|
**Related Terms**: [Other related terms]
|
||||||
|
|
||||||
|
**Usage Examples**:
|
||||||
|
- [Example 1]
|
||||||
|
- [Example 2]
|
||||||
|
|
||||||
|
**English Notation**: [English Term]
|
||||||
|
|
||||||
|
### [Term 2]
|
||||||
|
|
||||||
|
**Definition**: [Clear definition]
|
||||||
|
|
||||||
|
**Description**: [Detailed description]
|
||||||
|
|
||||||
|
**Related Terms**: [Other related terms]
|
||||||
|
|
||||||
|
**Usage Examples**:
|
||||||
|
- [Example 1]
|
||||||
|
- [Example 2]
|
||||||
|
|
||||||
|
## Technical Terms
|
||||||
|
|
||||||
|
Terms related to the technologies, frameworks, and tools used in the project.
|
||||||
|
|
||||||
|
### [Technology 1]
|
||||||
|
|
||||||
|
**Definition**: [Description of the technology]
|
||||||
|
|
||||||
|
**Official Site**: [URL]
|
||||||
|
|
||||||
|
**Usage in This Project**: [How it is used]
|
||||||
|
|
||||||
|
**Version**: [Version used]
|
||||||
|
|
||||||
|
**Related Documents**: [Links to internal documents]
|
||||||
|
|
||||||
|
### [Technology 2]
|
||||||
|
|
||||||
|
**Definition**: [Description of the technology]
|
||||||
|
|
||||||
|
**Official Site**: [URL]
|
||||||
|
|
||||||
|
**Usage in This Project**: [How it is used]
|
||||||
|
|
||||||
|
**Version**: [Version used]
|
||||||
|
|
||||||
|
## Abbreviations and Acronyms
|
||||||
|
|
||||||
|
### [Abbreviation 1]
|
||||||
|
|
||||||
|
**Full Name**: [Full Name]
|
||||||
|
|
||||||
|
**Meaning**: [Description]
|
||||||
|
|
||||||
|
**Usage in This Project**: [Where it is used]
|
||||||
|
|
||||||
|
### [Abbreviation 2]
|
||||||
|
|
||||||
|
**Full Name**: [Full Name]
|
||||||
|
|
||||||
|
**Meaning**: [Description]
|
||||||
|
|
||||||
|
**Usage in This Project**: [Where it is used]
|
||||||
|
|
||||||
|
## Architecture Terms
|
||||||
|
|
||||||
|
Terms related to system design and architecture.
|
||||||
|
|
||||||
|
### [Concept 1]
|
||||||
|
|
||||||
|
**Definition**: [Description of the architecture concept]
|
||||||
|
|
||||||
|
**Application in This Project**: [How it is implemented]
|
||||||
|
|
||||||
|
**Related Components**: [Related component names]
|
||||||
|
|
||||||
|
**Diagram**:
|
||||||
|
```
|
||||||
|
[ASCII diagram or Mermaid diagram]
|
||||||
|
```
|
||||||
|
|
||||||
|
### [Concept 2]
|
||||||
|
|
||||||
|
**Definition**: [Description of the architecture concept]
|
||||||
|
|
||||||
|
**Application in This Project**: [How it is implemented]
|
||||||
|
|
||||||
|
## Statuses and States
|
||||||
|
|
||||||
|
Definitions of various statuses used within the system.
|
||||||
|
|
||||||
|
### [Status Category 1]
|
||||||
|
|
||||||
|
| Status | Meaning | Transition Condition | Next State |
|
||||||
|
|----------|------|---------|---------|
|
||||||
|
| [State 1] | [Description] | [Condition] | [Next State] |
|
||||||
|
| [State 2] | [Description] | [Condition] | [Next State] |
|
||||||
|
|
||||||
|
**State Transition Diagram**:
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> State1
|
||||||
|
State1 --> State2: Condition A
|
||||||
|
State2 --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Model Terms
|
||||||
|
|
||||||
|
Terms related to databases and data structures.
|
||||||
|
|
||||||
|
### [Entity 1]
|
||||||
|
|
||||||
|
**Definition**: [Description of the entity]
|
||||||
|
|
||||||
|
**Key Fields**:
|
||||||
|
- `field1`: [Description]
|
||||||
|
- `field2`: [Description]
|
||||||
|
|
||||||
|
**Related Entities**: [Related entities]
|
||||||
|
|
||||||
|
**Constraints**: [Unique constraints, foreign key constraints, etc.]
|
||||||
|
|
||||||
|
## Errors and Exceptions
|
||||||
|
|
||||||
|
Errors and exceptions defined in the system.
|
||||||
|
|
||||||
|
### [Error Category 1]
|
||||||
|
|
||||||
|
**Class Name**: `[ErrorClassName]`
|
||||||
|
|
||||||
|
**Trigger Condition**: [When it occurs]
|
||||||
|
|
||||||
|
**Resolution**: [How users/developers should handle it]
|
||||||
|
|
||||||
|
**Error Code**: [If applicable]
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
throw new [ErrorClassName]('[message]');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Calculations and Algorithms (if applicable)
|
||||||
|
|
||||||
|
Terms related to specific calculation methods or algorithms.
|
||||||
|
|
||||||
|
### [Calculation Method 1]
|
||||||
|
|
||||||
|
**Definition**: [Description of the calculation method]
|
||||||
|
|
||||||
|
**Formula**:
|
||||||
|
```
|
||||||
|
[Formula]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation Location**: `src/[path]/[file].ts`
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
Input: [Example]
|
||||||
|
Output: [Result]
|
||||||
|
```
|
||||||
332
.opencode/skills/prd-writing/SKILL.md
Normal file
332
.opencode/skills/prd-writing/SKILL.md
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
---
|
||||||
|
name: prd-writing
|
||||||
|
description: A detailed guide and template for creating a Product Requirements Document (PRD). Use only when creating a PRD.
|
||||||
|
---
|
||||||
|
|
||||||
|
# PRD Writing Skill
|
||||||
|
|
||||||
|
This skill is a detailed guide for creating a high-quality Product Requirements Document (PRD).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before you begin creating a PRD, the following must be complete:
|
||||||
|
|
||||||
|
### The idea has been refined through brainstorming
|
||||||
|
|
||||||
|
The user must have completed refining the product idea through dialogue with opencode.
|
||||||
|
|
||||||
|
### docs/ideas/initial-requirements.md has been created
|
||||||
|
|
||||||
|
**Important**: The brainstorming content must be saved by the user in the following file:
|
||||||
|
|
||||||
|
**File path**: `docs/ideas/initial-requirements.md`
|
||||||
|
|
||||||
|
This file must include the following content:
|
||||||
|
- The basic idea of the product
|
||||||
|
- The problem to be solved
|
||||||
|
- An overview of the target users
|
||||||
|
- The main features to be implemented
|
||||||
|
- The scope of the MVP
|
||||||
|
|
||||||
|
When creating the PRD, refer to the content of this file to add detail.
|
||||||
|
|
||||||
|
## Priority of existing documents
|
||||||
|
|
||||||
|
**Important**: If an existing PRD is present at `docs/product-requirements.md`,
|
||||||
|
follow the priority order below:
|
||||||
|
|
||||||
|
1. **Existing PRD (`docs/product-requirements.md`)** - Highest priority
|
||||||
|
- Contains project-specific requirements
|
||||||
|
- Takes precedence over this skill's guide
|
||||||
|
|
||||||
|
2. **This skill's guide** - Reference material
|
||||||
|
- Generic templates and examples
|
||||||
|
- Use when there is no existing PRD, or as a supplement
|
||||||
|
|
||||||
|
**When creating from scratch**: Refer to this skill's template and guide
|
||||||
|
**When updating**: Update while preserving the structure and content of the existing PRD
|
||||||
|
|
||||||
|
## Output destination
|
||||||
|
|
||||||
|
Save the PRD you create to the following location:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/product-requirements.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template reference
|
||||||
|
|
||||||
|
When creating the PRD, use the following template: ./template.md
|
||||||
|
|
||||||
|
## PRD creation process
|
||||||
|
|
||||||
|
### 1. Review initial-requirements.md
|
||||||
|
|
||||||
|
First, review the initial requirements specification created by the user:
|
||||||
|
|
||||||
|
Read `docs/ideas/initial-requirements.md`
|
||||||
|
|
||||||
|
### 2. Generate the PRD draft
|
||||||
|
|
||||||
|
Based on the content of initial-requirements.md, generate the PRD following the template.
|
||||||
|
|
||||||
|
### 3. Review and improve the PRD
|
||||||
|
|
||||||
|
Review the generated PRD from the following perspectives:
|
||||||
|
|
||||||
|
#### Review perspectives
|
||||||
|
|
||||||
|
1. Is the product vision clear?
|
||||||
|
2. Are the target users specific?
|
||||||
|
3. Are the success metrics measurable?
|
||||||
|
4. Are the functional requirements detailed to an implementable level?
|
||||||
|
5. Are the non-functional requirements comprehensive?
|
||||||
|
|
||||||
|
#### Criteria for evaluating review results
|
||||||
|
|
||||||
|
Evaluate the generated PRD in the following format:
|
||||||
|
|
||||||
|
**✅ Strengths**
|
||||||
|
- A clear vision is described in a measurable and specific way
|
||||||
|
- The functional specifications are detailed to an implementation level
|
||||||
|
- KPIs are defined with quantitative metrics
|
||||||
|
|
||||||
|
**⚠️ Points that need improvement**
|
||||||
|
|
||||||
|
Ambiguity in functional requirements:
|
||||||
|
- Problem: There are areas where the concrete implementation specification is unclear
|
||||||
|
- Recommendation: Clearly state specific command specifications and error handling
|
||||||
|
|
||||||
|
Measurement method for success metrics:
|
||||||
|
- Problem: The measurement method is unclear
|
||||||
|
- Recommendation: Clearly state the measurement method and considerations for privacy
|
||||||
|
|
||||||
|
### 4. Improvement after review
|
||||||
|
|
||||||
|
Review the issues raised in the review one by one, and improve the areas that need more detail:
|
||||||
|
|
||||||
|
1. Review the raised issues one by one
|
||||||
|
2. Improve the areas that need more detail
|
||||||
|
3. After improving, conduct the review again
|
||||||
|
4. Repeat until there are no more issues
|
||||||
|
|
||||||
|
**Cautions**:
|
||||||
|
- Do not take the AI's review at face value; the final judgment must always be made by a human
|
||||||
|
- Clearly specify the review perspectives
|
||||||
|
- Have a human verify the validity of the improvement proposals
|
||||||
|
|
||||||
|
## Key points for creating a PRD
|
||||||
|
|
||||||
|
### 1. Specificity and measurability
|
||||||
|
|
||||||
|
Every requirement must be specific and measurable.
|
||||||
|
|
||||||
|
**Bad examples**:
|
||||||
|
- The system needs to be fast
|
||||||
|
- Users feel it is easy to use
|
||||||
|
|
||||||
|
**Good examples**:
|
||||||
|
- Command execution time: within 100ms (on an average PC environment)
|
||||||
|
- A new user can learn the basic operations within 5 minutes (measured by usability testing)
|
||||||
|
|
||||||
|
### 2. User-centered design
|
||||||
|
|
||||||
|
Every feature must solve a clear user problem.
|
||||||
|
|
||||||
|
**User story format**:
|
||||||
|
```
|
||||||
|
As a [user], I want [feature] so that [goal]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
As a developer, I want a CLI-based task management tool
|
||||||
|
so that I can manage tasks without leaving the terminal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Clarifying priorities
|
||||||
|
|
||||||
|
Set a priority for every feature:
|
||||||
|
|
||||||
|
- **P0 (Required)**: Features to include in the MVP (Minimum Viable Product). Without these, the product does not stand as a product
|
||||||
|
- **P1 (Important)**: Features that should be added immediately after the initial release
|
||||||
|
- **P2 (Nice to have)**: Features to consider adding in the future
|
||||||
|
|
||||||
|
## Details of the main PRD sections
|
||||||
|
|
||||||
|
### 1. Product overview
|
||||||
|
|
||||||
|
#### Components
|
||||||
|
|
||||||
|
1. **Name**: Product name and subtitle
|
||||||
|
2. **Product concept**: Three main concepts
|
||||||
|
3. **Product vision**: The world you aim for, in 3-5 sentences
|
||||||
|
4. **Purpose**: A list of specific purposes
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Name
|
||||||
|
**Devtask** - A task management CLI tool for developers
|
||||||
|
|
||||||
|
### Product concept
|
||||||
|
- Task management that is complete within the CLI: Complete all operations without leaving the terminal
|
||||||
|
- Automatic priority estimation: Automatically estimate priority from a task's due date, creation time, status change history, and so on
|
||||||
|
- A simple and fast operating feel: Complete operations with minimal keystrokes, with instant response
|
||||||
|
|
||||||
|
### Product vision
|
||||||
|
Provide a CLI tool that lets developers efficiently manage tasks without leaving the terminal.
|
||||||
|
Specialized for command-line operation, it delivers lightweight, fast task management that does not interrupt the development flow.
|
||||||
|
With automatic priority estimation, developers can focus on essential work.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Include a specific value proposition**
|
||||||
|
|
||||||
|
Bad example:
|
||||||
|
```
|
||||||
|
Build a convenient task management tool
|
||||||
|
```
|
||||||
|
|
||||||
|
Good example:
|
||||||
|
```
|
||||||
|
A CLI tool that lets developers manage tasks without leaving the terminal.
|
||||||
|
|
||||||
|
Value provided:
|
||||||
|
- Reduced context switching (zero GUI ↔ terminal switching)
|
||||||
|
- Improved work efficiency (no mouse operation, averaging a 30% time reduction)
|
||||||
|
- Integration with automation (can be embedded in shell scripts)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Target users (personas)
|
||||||
|
|
||||||
|
#### Required elements
|
||||||
|
|
||||||
|
1. **Basic attributes**: Age, occupation, years of experience
|
||||||
|
2. **Technology stack**: Tools and languages in use
|
||||||
|
3. **Current problems**: Specific pain points
|
||||||
|
4. **Expected solution**: What they want to become
|
||||||
|
5. **A typical daily workflow**
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Primary persona: Taro Tanaka (29, full-stack engineer)
|
||||||
|
- Freelancer running 3-5 projects in parallel
|
||||||
|
- Vim/Emacs + terminal environment
|
||||||
|
- Does not want to spend time on task management
|
||||||
|
- Prefers Markdown, Git, and CLI tools
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Success metrics (KPIs)
|
||||||
|
|
||||||
|
#### SMART principles
|
||||||
|
|
||||||
|
- **S**pecific: Clear about what is being measured
|
||||||
|
- **M**easurable: Can be measured numerically
|
||||||
|
- **A**chievable: A realistic target
|
||||||
|
- **R**elevant: Related to business goals
|
||||||
|
- **T**ime-bound: Sets a deadline for achievement
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Primary KPIs
|
||||||
|
- Daily active users (DAU): 100 (in 3 months)
|
||||||
|
- Task completion rate: 70% or higher
|
||||||
|
- Average number of commands executed per day: 10 or more
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Functional requirements
|
||||||
|
|
||||||
|
#### Core features (MVP)
|
||||||
|
|
||||||
|
Include the following for each feature:
|
||||||
|
- User story
|
||||||
|
- Acceptance criteria (in checklist form)
|
||||||
|
- Priority (P0/P1/P2)
|
||||||
|
|
||||||
|
**Format**:
|
||||||
|
```markdown
|
||||||
|
### [Feature name]
|
||||||
|
|
||||||
|
User story:
|
||||||
|
As a [user], I want [feature] so that [goal]
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
- [ ] Criterion 1 (measurable)
|
||||||
|
- [ ] Criterion 2 (measurable)
|
||||||
|
|
||||||
|
Priority: P0 (Required) / P1 (Important) / P2 (Nice to have)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CLI interface
|
||||||
|
|
||||||
|
For a CLI tool, include specific command examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic operations
|
||||||
|
devtask add "Task name" --due 2025-01-15 --priority high
|
||||||
|
devtask list
|
||||||
|
devtask next # Show the task to do now
|
||||||
|
devtask done <task-id>
|
||||||
|
devtask show <task-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Non-functional requirements
|
||||||
|
|
||||||
|
Describe them in a measurable form:
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```markdown
|
||||||
|
### Performance
|
||||||
|
- Command execution time: within 100ms (on an average PC environment)
|
||||||
|
- Task list display: within 1 second for up to 1000 items
|
||||||
|
|
||||||
|
### Usability
|
||||||
|
- A new user can learn the basic operations within 5 minutes
|
||||||
|
- All features can be checked via the help command
|
||||||
|
|
||||||
|
### Reliability
|
||||||
|
- Zero data loss (automatic backup)
|
||||||
|
- Rollback on error
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quality standards and checkpoints
|
||||||
|
|
||||||
|
To ensure the quality of the PRD, confirm the following checkpoints:
|
||||||
|
|
||||||
|
### Vision and goals
|
||||||
|
- [ ] Is the product vision clear and measurable?
|
||||||
|
- [ ] Is the specific value provided defined?
|
||||||
|
- [ ] Is the target market clear?
|
||||||
|
|
||||||
|
### Target users
|
||||||
|
- [ ] Is the persona specifically defined?
|
||||||
|
- [ ] Are the current problems and expected solution clear?
|
||||||
|
- [ ] Are the technology stack and daily workflow described?
|
||||||
|
|
||||||
|
### Success metrics
|
||||||
|
- [ ] Are KPIs defined following the SMART principles?
|
||||||
|
- [ ] Is the measurement method clear?
|
||||||
|
- [ ] Is an achievement deadline set?
|
||||||
|
|
||||||
|
### Functional requirements
|
||||||
|
- [ ] Is every feature described in user story form?
|
||||||
|
- [ ] Are acceptance criteria defined in a measurable form?
|
||||||
|
- [ ] Is the priority (P0/P1/P2) clearly set?
|
||||||
|
|
||||||
|
### Non-functional requirements
|
||||||
|
- [ ] Are performance standards defined with specific numbers?
|
||||||
|
- [ ] Are usability standards measurable?
|
||||||
|
- [ ] Are reliability and security requirements clear?
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Keys to success in creating a PRD:
|
||||||
|
|
||||||
|
1. **Create based on initial-requirements.md**: Refer to the brainstorming content created by the user
|
||||||
|
2. **Specificity and measurability**: Make every requirement clear
|
||||||
|
3. **User-centered**: Only features that solve user problems
|
||||||
|
4. **Clarifying priorities**: Classify by P0/P1/P2
|
||||||
|
5. **Review and improvement**: Self-review and final human judgment
|
||||||
|
6. **Applying the SMART principles**: Especially important when defining KPIs
|
||||||
116
.opencode/skills/prd-writing/template.md
Normal file
116
.opencode/skills/prd-writing/template.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Product Requirements Document
|
||||||
|
|
||||||
|
## Product overview
|
||||||
|
|
||||||
|
### Name
|
||||||
|
**[Product name]** - [Subtitle]
|
||||||
|
|
||||||
|
### Product concept
|
||||||
|
- **[Concept 1]**: [Description]
|
||||||
|
- **[Concept 2]**: [Description]
|
||||||
|
- **[Concept 3]**: [Description]
|
||||||
|
|
||||||
|
### Product vision
|
||||||
|
[Describe the world this product aims for, in 3-5 sentences]
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
- [Purpose 1]
|
||||||
|
- [Purpose 2]
|
||||||
|
- [Purpose 3]
|
||||||
|
|
||||||
|
## Target users
|
||||||
|
|
||||||
|
### Primary persona: [Name] ([Age], [Occupation])
|
||||||
|
- [Attribute 1: e.g., freelancer running 3-5 projects in parallel]
|
||||||
|
- [Attribute 2: e.g., Vim/Emacs + terminal environment]
|
||||||
|
- [Current problem: e.g., does not want to spend time on task management]
|
||||||
|
- [Expected solution]
|
||||||
|
- [A typical daily workflow]
|
||||||
|
|
||||||
|
## Success metrics (KPIs)
|
||||||
|
|
||||||
|
### Primary KPIs
|
||||||
|
- [Metric 1]: [Target value] ([Deadline])
|
||||||
|
- Example: Daily active users (DAU): 100 (in 3 months)
|
||||||
|
- [Metric 2]: [Target value] ([Deadline])
|
||||||
|
- Example: Task completion rate: 70% or higher
|
||||||
|
|
||||||
|
### Secondary KPIs
|
||||||
|
- [Metric 1]: [Target value] ([Deadline])
|
||||||
|
- [Metric 2]: [Target value] ([Deadline])
|
||||||
|
|
||||||
|
## Functional requirements
|
||||||
|
|
||||||
|
### Core features (MVP)
|
||||||
|
|
||||||
|
#### [Feature 1]
|
||||||
|
|
||||||
|
**User story**:
|
||||||
|
As a [user], I want [feature] so that [goal]
|
||||||
|
|
||||||
|
**Acceptance criteria**:
|
||||||
|
- [ ] [Criterion 1: described in a measurable form]
|
||||||
|
- [ ] [Criterion 2: described in a measurable form]
|
||||||
|
- [ ] [Criterion 3: described in a measurable form]
|
||||||
|
|
||||||
|
**Priority**: P0 (Required)
|
||||||
|
|
||||||
|
#### [Feature 2]
|
||||||
|
|
||||||
|
**User story**:
|
||||||
|
[Description]
|
||||||
|
|
||||||
|
**Acceptance criteria**:
|
||||||
|
- [ ] [Criterion 1]
|
||||||
|
- [ ] [Criterion 2]
|
||||||
|
|
||||||
|
**Priority**: P1 (Important)
|
||||||
|
|
||||||
|
### CLI interface (if applicable)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example of basic operations
|
||||||
|
[command] [subcommand] [options]
|
||||||
|
# Write specific command examples
|
||||||
|
```
|
||||||
|
|
||||||
|
### Future features (Post-MVP)
|
||||||
|
|
||||||
|
#### [Feature 3]
|
||||||
|
|
||||||
|
[Brief description]
|
||||||
|
|
||||||
|
**Priority**: P2 (Nice to have)
|
||||||
|
|
||||||
|
## Non-functional requirements
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- [Requirement 1]: [Measurable standard]
|
||||||
|
- Example: Command execution time: within 100ms
|
||||||
|
- [Requirement 2]: [Measurable standard]
|
||||||
|
- Example: Task list display: within 1 second for up to 1000 items
|
||||||
|
|
||||||
|
### Usability
|
||||||
|
- [Requirement 1]: [Measurable standard]
|
||||||
|
- Example: A new user can learn the basic operations within 5 minutes
|
||||||
|
- [Requirement 2]: [Measurable standard]
|
||||||
|
|
||||||
|
### Reliability
|
||||||
|
- [Requirement 1]: [Measurable standard]
|
||||||
|
- Example: Zero data loss (automatic backup)
|
||||||
|
- [Requirement 2]: [Measurable standard]
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- [Requirement 1]: [Measurable standard]
|
||||||
|
- [Requirement 2]: [Measurable standard]
|
||||||
|
|
||||||
|
### Scalability
|
||||||
|
- [Requirement 1]: [Measurable standard]
|
||||||
|
- [Requirement 2]: [Measurable standard]
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
Items explicitly considered out of scope:
|
||||||
|
- [Item 1]
|
||||||
|
- [Item 2]
|
||||||
|
- [Item 3]
|
||||||
51
.opencode/skills/repository-structure/SKILL.md
Normal file
51
.opencode/skills/repository-structure/SKILL.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
name: repository-structure
|
||||||
|
description: A detailed guide and template for creating a repository structure definition document. Use only when defining the repository structure.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Repository Structure Definition Skill
|
||||||
|
|
||||||
|
This skill is a detailed guide for defining a clear and maintainable repository structure.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before you begin defining the repository structure, confirm the following:
|
||||||
|
|
||||||
|
### Required documents
|
||||||
|
|
||||||
|
1. `docs/product-requirements.md` (PRD)
|
||||||
|
2. `docs/functional-design.md` (Functional Design Document)
|
||||||
|
3. `docs/architecture.md` (Architecture Design Document)
|
||||||
|
|
||||||
|
The repository structure defines a concrete directory layout that reflects the technology stack and system composition determined during architecture design.
|
||||||
|
|
||||||
|
## Priority of existing documents
|
||||||
|
|
||||||
|
**Important**: If an existing repository structure definition document is present at `docs/repository-structure.md`, follow the priority order below:
|
||||||
|
|
||||||
|
1. **Existing repository structure definition document (`docs/repository-structure.md`)** - Highest priority
|
||||||
|
- Contains the project-specific directory structure
|
||||||
|
- Takes precedence over this skill's guide
|
||||||
|
|
||||||
|
2. **This skill's guide** - Reference material
|
||||||
|
- Generic templates and examples
|
||||||
|
- Use when there is no existing definition document, or as a supplement
|
||||||
|
|
||||||
|
**When creating from scratch**: Refer to this skill's template and guide
|
||||||
|
**When updating**: Update while preserving the structure and content of the existing definition document
|
||||||
|
|
||||||
|
## Output destination
|
||||||
|
|
||||||
|
Save the repository structure definition document you create to the following location:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/repository-structure.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template reference
|
||||||
|
|
||||||
|
When creating the repository structure definition document, use the following template: ./template.md
|
||||||
|
|
||||||
|
## Detailed guide
|
||||||
|
|
||||||
|
For a more detailed creation guide, refer to the following file: ./guide.md
|
||||||
400
.opencode/skills/repository-structure/guide.md
Normal file
400
.opencode/skills/repository-structure/guide.md
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
# Repository Structure Document Authoring Guide
|
||||||
|
|
||||||
|
## Basic Principles
|
||||||
|
|
||||||
|
### 1. Clarify Roles
|
||||||
|
|
||||||
|
Each directory should have a single, well-defined role.
|
||||||
|
|
||||||
|
**Bad example**:
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── stuff/ # Vague
|
||||||
|
├── misc/ # Miscellaneous
|
||||||
|
└── utils/ # Too generic
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good example**:
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── commands/ # CLI command implementations
|
||||||
|
├── services/ # Business logic
|
||||||
|
├── repositories/ # Data persistence
|
||||||
|
└── validators/ # Input validation
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Enforce Layer Separation
|
||||||
|
|
||||||
|
Reflect the architecture's layer structure in the directory structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── ui/ # UI layer
|
||||||
|
│ └── cli/ # CLI implementation
|
||||||
|
├── services/ # Service layer
|
||||||
|
│ └── task/ # Task management service
|
||||||
|
└── repositories/ # Data layer
|
||||||
|
└── task/ # Task repository
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Split by Technical Concern (Baseline)
|
||||||
|
|
||||||
|
Split directories by related technical concerns:
|
||||||
|
|
||||||
|
**Basic structure**:
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── commands/ # CLI commands
|
||||||
|
├── services/ # Business logic
|
||||||
|
├── repositories/ # Data persistence
|
||||||
|
└── types/ # Type definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mapping to the layer structure**:
|
||||||
|
```
|
||||||
|
CLI/UI layer → commands/, cli/
|
||||||
|
Service layer → services/
|
||||||
|
Data layer → repositories/, storage/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Designing the Directory Structure
|
||||||
|
|
||||||
|
### Expressing the Layer Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Bad example: flat structure
|
||||||
|
src/
|
||||||
|
├── TaskCLI.ts
|
||||||
|
├── TaskService.ts
|
||||||
|
├── TaskRepository.ts
|
||||||
|
├── UserCLI.ts
|
||||||
|
├── UserService.ts
|
||||||
|
└── UserRepository.ts
|
||||||
|
|
||||||
|
// Good example: clear layers
|
||||||
|
src/
|
||||||
|
├── cli/
|
||||||
|
│ ├── TaskCLI.ts
|
||||||
|
│ └── UserCLI.ts
|
||||||
|
├── services/
|
||||||
|
│ ├── TaskService.ts
|
||||||
|
│ └── UserService.ts
|
||||||
|
└── repositories/
|
||||||
|
├── TaskRepository.ts
|
||||||
|
└── UserRepository.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Placement of the Test Directory
|
||||||
|
|
||||||
|
**Recommended structure**:
|
||||||
|
```
|
||||||
|
project/
|
||||||
|
├── src/
|
||||||
|
│ └── services/
|
||||||
|
│ └── TaskService.ts
|
||||||
|
└── tests/
|
||||||
|
├── unit/
|
||||||
|
│ └── services/
|
||||||
|
│ └── TaskService.test.ts
|
||||||
|
├── integration/
|
||||||
|
└── e2e/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
- Test code is separated from production code
|
||||||
|
- Easy to exclude tests at build time
|
||||||
|
- Can be organized by test type
|
||||||
|
|
||||||
|
## Naming Convention Best Practices
|
||||||
|
|
||||||
|
### Principles for Directory Names
|
||||||
|
|
||||||
|
**1. Use the plural form (layer directories)**
|
||||||
|
```
|
||||||
|
✅ services/
|
||||||
|
✅ repositories/
|
||||||
|
✅ controllers/
|
||||||
|
|
||||||
|
❌ service/
|
||||||
|
❌ repository/
|
||||||
|
❌ controller/
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: They hold multiple files
|
||||||
|
|
||||||
|
**2. Use kebab-case**
|
||||||
|
```
|
||||||
|
✅ task-management/
|
||||||
|
✅ user-authentication/
|
||||||
|
|
||||||
|
❌ TaskManagement/
|
||||||
|
❌ userAuthentication/
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: Compatibility with URLs and the file system
|
||||||
|
|
||||||
|
**3. Use specific names**
|
||||||
|
```
|
||||||
|
✅ validators/ # Input validation
|
||||||
|
✅ formatters/ # Data formatting
|
||||||
|
✅ parsers/ # Data parsing
|
||||||
|
|
||||||
|
❌ utils/ # Too generic
|
||||||
|
❌ helpers/ # Vague
|
||||||
|
❌ common/ # Meaningless
|
||||||
|
```
|
||||||
|
|
||||||
|
### Principles for File Names
|
||||||
|
|
||||||
|
**1. Class files: PascalCase + role suffix**
|
||||||
|
```typescript
|
||||||
|
// Service classes
|
||||||
|
TaskService.ts
|
||||||
|
UserAuthenticationService.ts
|
||||||
|
|
||||||
|
// Repository classes
|
||||||
|
TaskRepository.ts
|
||||||
|
UserRepository.ts
|
||||||
|
|
||||||
|
// Controller classes
|
||||||
|
TaskController.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Function files: camelCase + start with a verb**
|
||||||
|
```typescript
|
||||||
|
// Utility functions
|
||||||
|
formatDate.ts
|
||||||
|
validateEmail.ts
|
||||||
|
parseCommandArguments.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Type definition files: PascalCase or kebab-case**
|
||||||
|
```typescript
|
||||||
|
// Interface definitions
|
||||||
|
Task.ts
|
||||||
|
UserProfile.ts
|
||||||
|
|
||||||
|
// Type definition collections
|
||||||
|
task-types.d.ts
|
||||||
|
api-types.d.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Constant files: UPPER_SNAKE_CASE or kebab-case**
|
||||||
|
```typescript
|
||||||
|
// Constant definitions
|
||||||
|
API_ENDPOINTS.ts
|
||||||
|
ERROR_MESSAGES.ts
|
||||||
|
|
||||||
|
// or
|
||||||
|
api-endpoints.ts
|
||||||
|
error-messages.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Managing Dependencies
|
||||||
|
|
||||||
|
### Dependency Rules Between Layers
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example: dependency from an upper layer to a lower layer
|
||||||
|
// cli/TaskCLI.ts
|
||||||
|
import { TaskService } from '../services/TaskService';
|
||||||
|
|
||||||
|
class TaskCLI {
|
||||||
|
constructor(private taskService: TaskService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad example: dependency from a lower layer to an upper layer
|
||||||
|
// services/TaskService.ts
|
||||||
|
import { TaskCLI } from '../cli/TaskCLI'; // Forbidden!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Avoiding Circular Dependencies
|
||||||
|
|
||||||
|
**Problematic code**:
|
||||||
|
```typescript
|
||||||
|
// services/TaskService.ts
|
||||||
|
import { UserService } from './UserService';
|
||||||
|
|
||||||
|
export class TaskService {
|
||||||
|
constructor(private userService: UserService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// services/UserService.ts
|
||||||
|
import { TaskService } from './TaskService'; // Circular dependency!
|
||||||
|
|
||||||
|
export class UserService {
|
||||||
|
constructor(private taskService: TaskService) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution 1: Extract shared type definitions**
|
||||||
|
```typescript
|
||||||
|
// types/Service.ts
|
||||||
|
export interface ITaskService { /* ... */ }
|
||||||
|
export interface IUserService { /* ... */ }
|
||||||
|
|
||||||
|
// services/TaskService.ts
|
||||||
|
import type { IUserService } from '../types/Service';
|
||||||
|
|
||||||
|
export class TaskService {
|
||||||
|
constructor(private userService: IUserService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// services/UserService.ts
|
||||||
|
import type { ITaskService } from '../types/Service';
|
||||||
|
|
||||||
|
export class UserService {
|
||||||
|
constructor(private taskService: ITaskService) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution 2: Rethink the dependencies**
|
||||||
|
```typescript
|
||||||
|
// Extract shared functionality into a separate service
|
||||||
|
// services/NotificationService.ts
|
||||||
|
export class NotificationService {
|
||||||
|
notifyTaskAssignment(taskId: string, userId: string): void {
|
||||||
|
// Notification handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// services/TaskService.ts
|
||||||
|
import { NotificationService } from './NotificationService';
|
||||||
|
|
||||||
|
export class TaskService {
|
||||||
|
constructor(private notificationService: NotificationService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// services/UserService.ts
|
||||||
|
import { NotificationService } from './NotificationService';
|
||||||
|
|
||||||
|
export class UserService {
|
||||||
|
constructor(private notificationService: NotificationService) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scaling Strategy
|
||||||
|
|
||||||
|
### Recommended Structure
|
||||||
|
|
||||||
|
**Standard pattern**:
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── commands/
|
||||||
|
│ └── TaskCommand.ts
|
||||||
|
├── services/
|
||||||
|
│ ├── TaskService.ts
|
||||||
|
│ └── UserService.ts
|
||||||
|
├── repositories/
|
||||||
|
│ ├── TaskRepository.ts
|
||||||
|
│ └── UserRepository.ts
|
||||||
|
├── types/
|
||||||
|
│ ├── Task.ts
|
||||||
|
│ └── User.ts
|
||||||
|
├── validators/
|
||||||
|
│ └── TaskValidator.ts
|
||||||
|
└── index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
- Responsibilities are clear per layer
|
||||||
|
- No later refactoring required
|
||||||
|
- Easy to standardize across a team
|
||||||
|
|
||||||
|
### Timing for Module Separation
|
||||||
|
|
||||||
|
**Signs that separation should be considered**:
|
||||||
|
1. A directory contains 10 or more files
|
||||||
|
2. Related functionality is grouped together
|
||||||
|
3. It can be tested independently
|
||||||
|
4. It has few dependencies on other features
|
||||||
|
|
||||||
|
**Separation procedure**:
|
||||||
|
```typescript
|
||||||
|
// Before: everything placed in services/
|
||||||
|
services/
|
||||||
|
├── TaskService.ts
|
||||||
|
├── TaskValidationService.ts
|
||||||
|
├── TaskNotificationService.ts
|
||||||
|
├── UserService.ts
|
||||||
|
└── UserAuthService.ts
|
||||||
|
|
||||||
|
// After: modularized by feature
|
||||||
|
modules/
|
||||||
|
├── task/
|
||||||
|
│ ├── TaskService.ts
|
||||||
|
│ ├── TaskValidationService.ts
|
||||||
|
│ └── TaskNotificationService.ts
|
||||||
|
└── user/
|
||||||
|
├── UserService.ts
|
||||||
|
└── UserAuthService.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Handling Special Cases
|
||||||
|
|
||||||
|
### Placement of Shared Code
|
||||||
|
|
||||||
|
**shared/ or common/ directory**
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── shared/
|
||||||
|
│ ├── utils/ # General-purpose utilities
|
||||||
|
│ ├── types/ # Shared type definitions
|
||||||
|
│ └── constants/ # Shared constants
|
||||||
|
├── commands/
|
||||||
|
├── services/
|
||||||
|
└── repositories/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rules**:
|
||||||
|
- Only things genuinely used across multiple layers
|
||||||
|
- Do not include anything used in only a single layer
|
||||||
|
|
||||||
|
### Managing Configuration Files (when applicable)
|
||||||
|
|
||||||
|
```
|
||||||
|
config/
|
||||||
|
├── default.ts # Default settings
|
||||||
|
└── constants.ts # Constant definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Scripts (when applicable)
|
||||||
|
|
||||||
|
```
|
||||||
|
scripts/
|
||||||
|
├── build.sh # Build script
|
||||||
|
└── dev-tools.ts # Development helper script
|
||||||
|
```
|
||||||
|
|
||||||
|
## Document Placement
|
||||||
|
|
||||||
|
### Document Types and Their Locations
|
||||||
|
|
||||||
|
**Project root**:
|
||||||
|
- `README.md`: Project overview
|
||||||
|
- `CONTRIBUTING.md`: Contribution guide
|
||||||
|
- `LICENSE`: License
|
||||||
|
|
||||||
|
**docs/ directory**:
|
||||||
|
- `product-requirements.md`: PRD
|
||||||
|
- `functional-design.md`: Functional design document
|
||||||
|
- `architecture.md`: Architecture design document
|
||||||
|
- `repository-structure.md`: This document
|
||||||
|
- `development-guidelines.md`: Development guidelines
|
||||||
|
- `glossary.md`: Glossary
|
||||||
|
|
||||||
|
**Within the source code**:
|
||||||
|
- TSDoc/JSDoc comments: Descriptions of functions and classes
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Each directory's role is clearly defined
|
||||||
|
- [ ] The layer structure is reflected in the directories
|
||||||
|
- [ ] Naming conventions are consistent
|
||||||
|
- [ ] The placement policy for test code is decided
|
||||||
|
- [ ] Dependency rules are clear
|
||||||
|
- [ ] There are no circular dependencies
|
||||||
|
- [ ] A scaling strategy has been considered
|
||||||
|
- [ ] Placement rules for shared code are defined
|
||||||
|
- [ ] A management method for configuration files is decided
|
||||||
|
- [ ] Document locations are clear
|
||||||
306
.opencode/skills/repository-structure/template.md
Normal file
306
.opencode/skills/repository-structure/template.md
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
# Repository Structure Document
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
project-root/
|
||||||
|
├── src/ # Source code
|
||||||
|
│ ├── [layer1]/ # [Description]
|
||||||
|
│ ├── [layer2]/ # [Description]
|
||||||
|
│ └── [layer3]/ # [Description]
|
||||||
|
├── tests/ # Test code
|
||||||
|
│ ├── unit/ # Unit tests
|
||||||
|
│ ├── integration/ # Integration tests
|
||||||
|
│ └── e2e/ # E2E tests
|
||||||
|
├── docs/ # Project documentation
|
||||||
|
├── config/ # Configuration files
|
||||||
|
└── scripts/ # Build/deploy scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Details
|
||||||
|
|
||||||
|
### src/ (Source Code Directory)
|
||||||
|
|
||||||
|
#### [Directory 1]
|
||||||
|
|
||||||
|
**Role**: [Description]
|
||||||
|
|
||||||
|
**Files placed here**:
|
||||||
|
- [File pattern 1]: [Description]
|
||||||
|
- [File pattern 2]: [Description]
|
||||||
|
|
||||||
|
**Naming conventions**:
|
||||||
|
- [Rule 1]
|
||||||
|
- [Rule 2]
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- May depend on: [Directory name]
|
||||||
|
- Must not depend on: [Directory name]
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
[Directory name]/
|
||||||
|
├── [example-file1].ts
|
||||||
|
└── [example-file2].ts
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [Directory 2]
|
||||||
|
|
||||||
|
**Role**: [Description]
|
||||||
|
|
||||||
|
**Files placed here**:
|
||||||
|
- [File pattern 1]: [Description]
|
||||||
|
|
||||||
|
**Naming conventions**:
|
||||||
|
- [Rule 1]
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- May depend on: [Directory name]
|
||||||
|
- Must not depend on: [Directory name]
|
||||||
|
|
||||||
|
### tests/ (Test Directory)
|
||||||
|
|
||||||
|
#### unit/
|
||||||
|
|
||||||
|
**Role**: Placement of unit tests
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```
|
||||||
|
tests/unit/
|
||||||
|
└── src/ # Same structure as the src directory
|
||||||
|
└── [layer]/
|
||||||
|
└── [filename].test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Naming conventions**:
|
||||||
|
- Pattern: `[name of file under test].test.ts`
|
||||||
|
- Example: `TaskService.ts` → `TaskService.test.ts`
|
||||||
|
|
||||||
|
#### integration/
|
||||||
|
|
||||||
|
**Role**: Placement of integration tests
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```
|
||||||
|
tests/integration/
|
||||||
|
└── [feature]/ # Split into directories by feature
|
||||||
|
└── [scenario].test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
#### e2e/
|
||||||
|
|
||||||
|
**Role**: Placement of E2E tests
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```
|
||||||
|
tests/e2e/
|
||||||
|
└── [user-scenario]/ # By user scenario
|
||||||
|
└── [flow].test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### docs/ (Documentation Directory)
|
||||||
|
|
||||||
|
**Documents placed here**:
|
||||||
|
- `product-requirements.md`: Product Requirements Document
|
||||||
|
- `functional-design.md`: Functional design document
|
||||||
|
- `architecture.md`: Architecture design document
|
||||||
|
- `repository-structure.md`: Repository structure document (this document)
|
||||||
|
- `development-guidelines.md`: Development guidelines
|
||||||
|
- `glossary.md`: Glossary
|
||||||
|
|
||||||
|
### config/ (Configuration File Directory - when applicable)
|
||||||
|
|
||||||
|
**Files placed here**:
|
||||||
|
- Configuration files
|
||||||
|
- Constant definition files
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
config/
|
||||||
|
├── default.ts
|
||||||
|
└── constants.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### scripts/ (Script Directory - when applicable)
|
||||||
|
|
||||||
|
**Files placed here**:
|
||||||
|
- Build scripts
|
||||||
|
- Development helper scripts
|
||||||
|
|
||||||
|
## File Placement Rules
|
||||||
|
|
||||||
|
### Source Files
|
||||||
|
|
||||||
|
| File type | Location | Naming convention | Example |
|
||||||
|
|------------|--------|---------|-----|
|
||||||
|
| [Type 1] | [Directory] | [Rule] | [Example] |
|
||||||
|
| [Type 2] | [Directory] | [Rule] | [Example] |
|
||||||
|
|
||||||
|
### Test Files
|
||||||
|
|
||||||
|
| Test type | Location | Naming convention | Example |
|
||||||
|
|-----------|--------|---------|-----|
|
||||||
|
| Unit test | tests/unit/ | [target].test.ts | TaskService.test.ts |
|
||||||
|
| Integration test | tests/integration/ | [feature].test.ts | task-crud.test.ts |
|
||||||
|
| E2E test | tests/e2e/ | [scenario].test.ts | user-workflow.test.ts |
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
| File type | Location | Naming convention |
|
||||||
|
|------------|--------|---------|
|
||||||
|
| Environment config | config/environments/ | [environment-name].ts |
|
||||||
|
| Tool config | Project root | [tool-name].config.js |
|
||||||
|
| Type definitions | src/types/ | [target].d.ts |
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
### Directory Names
|
||||||
|
|
||||||
|
- **Layer directories**: plural, kebab-case
|
||||||
|
- Example: `services/`, `repositories/`, `controllers/`
|
||||||
|
- **Feature directories**: singular, kebab-case
|
||||||
|
- Example: `task-management/`, `user-authentication/`
|
||||||
|
|
||||||
|
### File Names
|
||||||
|
|
||||||
|
- **Class files**: PascalCase
|
||||||
|
- Example: `TaskService.ts`, `UserRepository.ts`
|
||||||
|
- **Function files**: camelCase
|
||||||
|
- Example: `formatDate.ts`, `validateEmail.ts`
|
||||||
|
- **Constant files**: UPPER_SNAKE_CASE
|
||||||
|
- Example: `API_ENDPOINTS.ts`, `ERROR_MESSAGES.ts`
|
||||||
|
|
||||||
|
### Test File Names
|
||||||
|
|
||||||
|
- Pattern: `[target].test.ts` or `[target].spec.ts`
|
||||||
|
- Example: `TaskService.test.ts`, `formatDate.spec.ts`
|
||||||
|
|
||||||
|
## Dependency Rules
|
||||||
|
|
||||||
|
### Dependencies Between Layers
|
||||||
|
|
||||||
|
```
|
||||||
|
UI layer
|
||||||
|
↓ (OK)
|
||||||
|
Service layer
|
||||||
|
↓ (OK)
|
||||||
|
Data layer
|
||||||
|
```
|
||||||
|
|
||||||
|
**Forbidden dependencies**:
|
||||||
|
- Data layer → Service layer (❌)
|
||||||
|
- Data layer → UI layer (❌)
|
||||||
|
- Service layer → UI layer (❌)
|
||||||
|
|
||||||
|
### Dependencies Between Modules
|
||||||
|
|
||||||
|
**No circular dependencies**:
|
||||||
|
```typescript
|
||||||
|
// ❌ Bad example: circular dependency
|
||||||
|
// fileA.ts
|
||||||
|
import { funcB } from './fileB';
|
||||||
|
|
||||||
|
// fileB.ts
|
||||||
|
import { funcA } from './fileA'; // Circular dependency
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```typescript
|
||||||
|
// ✅ Good example: extract a shared module
|
||||||
|
// shared.ts
|
||||||
|
export interface SharedType { /* ... */ }
|
||||||
|
|
||||||
|
// fileA.ts
|
||||||
|
import { SharedType } from './shared';
|
||||||
|
|
||||||
|
// fileB.ts
|
||||||
|
import { SharedType } from './shared';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scaling Strategy
|
||||||
|
|
||||||
|
### Adding Features
|
||||||
|
|
||||||
|
Placement policy when adding new features:
|
||||||
|
|
||||||
|
1. **Small features**: place in an existing directory
|
||||||
|
2. **Medium features**: create a subdirectory within a layer
|
||||||
|
3. **Large features**: separate into an independent module
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── services/
|
||||||
|
│ ├── TaskService.ts # Existing feature
|
||||||
|
│ └── task-management/ # Separation of a medium feature
|
||||||
|
│ ├── TaskService.ts
|
||||||
|
│ ├── SubtaskService.ts
|
||||||
|
│ └── TaskCategoryService.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing File Size
|
||||||
|
|
||||||
|
**Guidelines for splitting files**:
|
||||||
|
- A single file: 300 lines or fewer recommended
|
||||||
|
- 300-500 lines: consider refactoring
|
||||||
|
- 500 lines or more: splitting strongly recommended
|
||||||
|
|
||||||
|
**How to split**:
|
||||||
|
```typescript
|
||||||
|
// Bad example: all functionality in one file
|
||||||
|
// TaskService.ts (800 lines)
|
||||||
|
|
||||||
|
// Good example: split by responsibility
|
||||||
|
// TaskService.ts (200 lines) - CRUD operations
|
||||||
|
// TaskValidationService.ts (150 lines) - validation
|
||||||
|
// TaskNotificationService.ts (100 lines) - notification handling
|
||||||
|
```
|
||||||
|
|
||||||
|
## Special Directories
|
||||||
|
|
||||||
|
### .steering/ (Steering Files)
|
||||||
|
|
||||||
|
**Role**: Define "what to do this time" for a specific development task
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```
|
||||||
|
.steering/
|
||||||
|
└── [YYYYMMDD]-[task-name]/
|
||||||
|
├── requirements.md # Requirements for this task
|
||||||
|
├── design.md # Design of the changes
|
||||||
|
└── tasklist.md # Task list
|
||||||
|
```
|
||||||
|
|
||||||
|
**Naming convention**: `20250115-add-user-profile` format
|
||||||
|
|
||||||
|
### .opencode/ (opencode Settings)
|
||||||
|
|
||||||
|
**Role**: opencode settings and customization
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```
|
||||||
|
.opencode/
|
||||||
|
├── command/ # Slash commands
|
||||||
|
├── skills/ # Task-specific skills
|
||||||
|
└── agent/ # Subagent definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exclusion Settings
|
||||||
|
|
||||||
|
### .gitignore
|
||||||
|
|
||||||
|
Files that should be excluded in the project:
|
||||||
|
- `node_modules/`
|
||||||
|
- `dist/`
|
||||||
|
- `.env`
|
||||||
|
- `.steering/` (temporary files for task management)
|
||||||
|
- `*.log`
|
||||||
|
- `.DS_Store`
|
||||||
|
|
||||||
|
### .prettierignore, .eslintignore
|
||||||
|
|
||||||
|
Files that should be excluded by tooling:
|
||||||
|
- `dist/`
|
||||||
|
- `node_modules/`
|
||||||
|
- `.steering/`
|
||||||
|
- `coverage/`
|
||||||
383
.opencode/skills/steering/SKILL.md
Normal file
383
.opencode/skills/steering/SKILL.md
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
---
|
||||||
|
name: steering
|
||||||
|
description: A skill for recording the work plan and task list for each work instruction in documents. Load it during work planning, implementation, and verification triggered by user instructions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Steering Skill
|
||||||
|
|
||||||
|
A skill that supports implementation based on steering files (`.steering/`) and reliably manages progress in tasklist.md.
|
||||||
|
|
||||||
|
## Purpose of This Skill
|
||||||
|
|
||||||
|
- Support creation of steering files (requirements.md, design.md, tasklist.md)
|
||||||
|
- Manage incremental implementation based on tasklist.md
|
||||||
|
- **Automatically track progress and enforce tasklist.md updates**
|
||||||
|
- Record a retrospective after implementation is complete
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
Use this skill at the following times:
|
||||||
|
|
||||||
|
1. **During work planning**: when creating steering files
|
||||||
|
2. **During implementation**: when implementing according to tasklist.md
|
||||||
|
3. **During verification**: when recording the retrospective after implementation is complete
|
||||||
|
|
||||||
|
## Mode 1: Creating Steering Files
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Create steering files for a new feature or change.
|
||||||
|
|
||||||
|
### Procedure
|
||||||
|
|
||||||
|
1. **Check the steering directory**
|
||||||
|
```
|
||||||
|
Get the current date and create a directory in the form `.steering/[YYYYMMDD]-[feature-name]/`
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Review the persistent documents**
|
||||||
|
- `docs/product-requirements.md`
|
||||||
|
- `docs/functional-design.md`
|
||||||
|
- `docs/architecture.md`
|
||||||
|
- `docs/repository-structure.md`
|
||||||
|
- `docs/development-guidelines.md`
|
||||||
|
|
||||||
|
Read these to understand the project's direction
|
||||||
|
|
||||||
|
3. **Create files from templates**
|
||||||
|
|
||||||
|
Load the following templates, replace the placeholders with concrete content, and create the files:
|
||||||
|
|
||||||
|
- `.opencode/skills/steering/templates/requirements.md` → `.steering/[date]-[feature-name]/requirements.md`
|
||||||
|
- `.opencode/skills/steering/templates/design.md` → `.steering/[date]-[feature-name]/design.md`
|
||||||
|
- `.opencode/skills/steering/templates/tasklist.md` → `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
|
||||||
|
4. **Detail out tasklist.md**
|
||||||
|
|
||||||
|
Based on requirements.md and design.md, flesh out tasklist.md:
|
||||||
|
- Describe the tasks of each phase concretely
|
||||||
|
- Make subtasks clear as well
|
||||||
|
- State the order of implementation
|
||||||
|
|
||||||
|
## Mode 2: Implementation (Most Important)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Proceed with implementation according to tasklist.md and **reliably record progress in the document**.
|
||||||
|
|
||||||
|
### 🚨 Important Principles
|
||||||
|
|
||||||
|
**MUST**:
|
||||||
|
- Implement with tasklist.md always open
|
||||||
|
- When starting a task, always update `[ ]`→`[x]` using the edit tool
|
||||||
|
- When completing a task, always record completion using the edit tool
|
||||||
|
- **Keep working until all tasks in tasklist.md are complete**
|
||||||
|
- NEVER: do not move on to the next task without updating tasklist.md
|
||||||
|
|
||||||
|
**NEVER**:
|
||||||
|
- Proceed with implementation without looking at tasklist.md
|
||||||
|
- Manage progress with the todowrite tool alone (todowrite is a helper; tasklist.md is the official record)
|
||||||
|
- Update multiple tasks in batch (update in real time)
|
||||||
|
- **Skip a task for reasons such as "due to time constraints" or "planned as a separate task"**
|
||||||
|
- **Finish work while leaving incomplete tasks (`[ ]`)**
|
||||||
|
|
||||||
|
### 🚨 Principle of Fully Completing Tasks
|
||||||
|
|
||||||
|
**Rules that absolutely must be followed**:
|
||||||
|
|
||||||
|
1. **Keep working until all tasks in tasklist.md are complete**
|
||||||
|
- Continue implementation until every task is `[x]`
|
||||||
|
- Do not skip for reasons such as "it takes too long" or "it's difficult"
|
||||||
|
- Do not write a retrospective while incomplete tasks remain
|
||||||
|
|
||||||
|
2. **Skipping tasks is forbidden in principle**
|
||||||
|
- "Planned as a separate task due to time constraints" is forbidden
|
||||||
|
- "Postponed because the implementation is too complex" is forbidden
|
||||||
|
- Reasons such as "it's difficult, do it later" or "testing is a hassle" are forbidden
|
||||||
|
- Skipping is permitted only for technical reasons (see below)
|
||||||
|
|
||||||
|
3. **How to handle a task that is too large**
|
||||||
|
- Split the task into smaller subtasks
|
||||||
|
- Add the split subtasks to tasklist.md
|
||||||
|
- Complete the subtasks one by one
|
||||||
|
|
||||||
|
4. **Skipping is permitted only when a task becomes unnecessary for technical reasons**
|
||||||
|
|
||||||
|
Skipping is possible only when one of the following technical reasons applies:
|
||||||
|
- A change in the implementation approach made the feature itself unnecessary
|
||||||
|
- An architecture change replaced it with a different implementation method
|
||||||
|
- A change in dependencies made the task impossible to execute
|
||||||
|
- An upper-level design change made this task meaningless
|
||||||
|
|
||||||
|
Skip procedure:
|
||||||
|
- Clearly state the technical reason in tasklist.md and mark it as skipped
|
||||||
|
- Example: `- [x] ~~task name~~ (unnecessary due to a change in approach: the architecture was changed from X to Y, so this layer is no longer needed)`
|
||||||
|
- Record the reason for the change in detail in the retrospective section
|
||||||
|
|
||||||
|
5. **Bad examples when incomplete tasks remain**
|
||||||
|
```markdown
|
||||||
|
## Post-Implementation Retrospective
|
||||||
|
**Tasks not implemented**:
|
||||||
|
- Implementing tests (planned as a separate task due to time constraints) ❌ Absolutely not allowed
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **What correct completion looks like**
|
||||||
|
- All tasks are `[x]`
|
||||||
|
- There is no "tasks not implemented" entry in the retrospective section
|
||||||
|
- If the implementation approach changed, the reason is clearly stated
|
||||||
|
|
||||||
|
### Implementation Flow
|
||||||
|
|
||||||
|
#### Step 1: Load tasklist.md
|
||||||
|
|
||||||
|
Read `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
|
||||||
|
Grasp the overall task structure and identify the next task to work on.
|
||||||
|
|
||||||
|
#### Step 2: Start task management with todowrite
|
||||||
|
|
||||||
|
Based on the contents of tasklist.md, create a task list with the todowrite tool:
|
||||||
|
- This is an auxiliary, internal note
|
||||||
|
- **tasklist.md is the official document**
|
||||||
|
|
||||||
|
#### Step 3: Task Loop (repeat for each task)
|
||||||
|
|
||||||
|
**3-1. Check the next task**
|
||||||
|
```
|
||||||
|
Read tasklist.md and identify the next incomplete task (`[ ]`)
|
||||||
|
```
|
||||||
|
|
||||||
|
**3-2. Record the task start in tasklist.md (required)**
|
||||||
|
```
|
||||||
|
Use the edit tool to update the relevant line in tasklist.md from `[ ]`→`[x]`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
old_string: "- [ ] Implement StorageService"
|
||||||
|
new_string: "- [x] Implement StorageService"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: Immediately after running the edit tool, confirm that the update succeeded.
|
||||||
|
|
||||||
|
**3-3. Update the status in todowrite as well**
|
||||||
|
```
|
||||||
|
Change the relevant task to "in_progress" with the todowrite tool
|
||||||
|
```
|
||||||
|
|
||||||
|
**3-4. Carry out the implementation**
|
||||||
|
```
|
||||||
|
Implement according to the development guidelines (docs/development-guidelines.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
**3-5. Record task completion in tasklist.md (required)**
|
||||||
|
```
|
||||||
|
After implementation is complete, always update tasklist.md with the edit tool to record completion
|
||||||
|
|
||||||
|
If there are subtasks, update each subtask individually as well
|
||||||
|
```
|
||||||
|
|
||||||
|
**3-6. Update the status in todowrite as well**
|
||||||
|
```
|
||||||
|
Change the relevant task to "completed" with the todowrite tool
|
||||||
|
```
|
||||||
|
|
||||||
|
**3-7. On to the next task**
|
||||||
|
```
|
||||||
|
Return to Step 3-1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 4: Checking at Phase Completion
|
||||||
|
|
||||||
|
When each phase (e.g., Phase 1, Phase 2) is complete:
|
||||||
|
|
||||||
|
1. **Load tasklist.md and check progress**
|
||||||
|
```
|
||||||
|
Read `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check completed tasks**
|
||||||
|
- Are all tasks `[x]`?
|
||||||
|
- Are there any tasks that were overlooked?
|
||||||
|
|
||||||
|
3. **Report to the user**
|
||||||
|
```
|
||||||
|
"Phase 1 is complete. Please check the progress in tasklist.md."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 4.5: All-Tasks-Complete Check (required)
|
||||||
|
|
||||||
|
**Always run this after implementing all phases, before writing the retrospective**:
|
||||||
|
|
||||||
|
1. **Load tasklist.md**
|
||||||
|
```
|
||||||
|
Read `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check for incomplete tasks (`[ ]`)**
|
||||||
|
- Are all tasks `[x]`?
|
||||||
|
- Is there even one `[ ]` remaining?
|
||||||
|
|
||||||
|
3. **If an incomplete task is found**
|
||||||
|
|
||||||
|
**❌ What you must not do**:
|
||||||
|
- Write "planned as a separate task due to time constraints" in the retrospective
|
||||||
|
- Ignore the incomplete task and move on to the next step
|
||||||
|
|
||||||
|
**✅ The correct response**:
|
||||||
|
|
||||||
|
**Pattern A: Implement the task**
|
||||||
|
```
|
||||||
|
Return to Step 3 (the task loop) and implement the incomplete task
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern B: If the task is too large**
|
||||||
|
```
|
||||||
|
1. Split the task into smaller subtasks
|
||||||
|
2. Add the split subtasks to tasklist.md
|
||||||
|
3. Complete the subtasks one by one
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern C: Only when a task became unnecessary for technical reasons**
|
||||||
|
|
||||||
|
Skipping is possible only when one of the following technical reasons applies:
|
||||||
|
- A change in the implementation approach made the feature itself unnecessary
|
||||||
|
- An architecture change replaced it with a different implementation method
|
||||||
|
- A change in dependencies made the task impossible to execute
|
||||||
|
|
||||||
|
Skip procedure:
|
||||||
|
```
|
||||||
|
1. Clearly state the technical reason in tasklist.md:
|
||||||
|
"- [x] ~~task name~~ (unnecessary due to a change in approach: describe the specific technical reason in detail)"
|
||||||
|
2. Record the reason for the change in detail in the retrospective section
|
||||||
|
3. Clearly describe why this task became unnecessary and what it was replaced with
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Proceed only after confirming all tasks are complete**
|
||||||
|
```
|
||||||
|
Confirm that every task is `[x]` before proceeding to Step 5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 5: After All Tasks Are Complete
|
||||||
|
|
||||||
|
1. **Final check**
|
||||||
|
```
|
||||||
|
Read `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm that every task is `[x]`
|
||||||
|
|
||||||
|
2. **Record in the retrospective section**
|
||||||
|
```
|
||||||
|
Update the "Post-Implementation Retrospective" section of tasklist.md with the edit tool:
|
||||||
|
- Implementation completion date
|
||||||
|
- Differences between plan and actual
|
||||||
|
- Lessons learned
|
||||||
|
- Improvement suggestions for next time
|
||||||
|
```
|
||||||
|
|
||||||
|
### Self-Check During Implementation
|
||||||
|
|
||||||
|
Every 5 tasks, check the following:
|
||||||
|
|
||||||
|
- [ ] Did you recently update tasklist.md? (within 5 tasks since the last update)
|
||||||
|
- [ ] Is progress reflected in the document? (verify with the read tool)
|
||||||
|
- [ ] Can the user understand the progress by looking at tasklist.md?
|
||||||
|
|
||||||
|
## Mode 3: Retrospective
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
After implementation is complete, record a retrospective in tasklist.md.
|
||||||
|
|
||||||
|
### Procedure
|
||||||
|
|
||||||
|
1. **Load tasklist.md**
|
||||||
|
```
|
||||||
|
Read `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Write the retrospective content**
|
||||||
|
- Implementation completion date
|
||||||
|
- Differences between plan and actual (points that differed from the plan)
|
||||||
|
- Lessons learned (technical insights, process improvements)
|
||||||
|
- Improvement suggestions for next time
|
||||||
|
|
||||||
|
3. **Update with the edit tool**
|
||||||
|
```
|
||||||
|
Update the "Post-Implementation Retrospective" section of tasklist.md
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Report to the user**
|
||||||
|
```
|
||||||
|
"I have recorded the retrospective in tasklist.md. Please check the content."
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### If you forgot to update tasklist.md
|
||||||
|
|
||||||
|
If you notice during implementation that you forgot to update tasklist.md:
|
||||||
|
|
||||||
|
1. **Update immediately**
|
||||||
|
```
|
||||||
|
Read `.steering/[date]-[feature-name]/tasklist.md`
|
||||||
|
Identify the completed tasks and update them all to `[x]` with the edit tool
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Report to the user**
|
||||||
|
```
|
||||||
|
"The tasklist.md update was delayed, so I have reflected the current progress."
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Prevent recurrence**
|
||||||
|
- Update reliably from the next task onward
|
||||||
|
- Strictly follow the self-check every 5 tasks
|
||||||
|
|
||||||
|
### Divergence between tasklist.md and the implementation
|
||||||
|
|
||||||
|
When the plan and the implementation differ significantly:
|
||||||
|
|
||||||
|
1. **Add an annotation to tasklist.md**
|
||||||
|
```
|
||||||
|
Add an annotation to the relevant task with the edit tool:
|
||||||
|
"- [x] task name (changed implementation method: reason)"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Add new tasks as needed**
|
||||||
|
```
|
||||||
|
Add new tasks with the edit tool
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Update design.md too**
|
||||||
|
```
|
||||||
|
If the design change is significant, update design.md as well
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist (Most Important)
|
||||||
|
|
||||||
|
Always check before implementation:
|
||||||
|
|
||||||
|
- [ ] Did you load tasklist.md?
|
||||||
|
- [ ] Did you identify the next task?
|
||||||
|
- [ ] Did you update with the edit tool when starting the task?
|
||||||
|
|
||||||
|
Always check after implementation:
|
||||||
|
|
||||||
|
- [ ] Did you update with the edit tool when completing the task?
|
||||||
|
- [ ] Did you check the progress in tasklist.md?
|
||||||
|
- [ ] Is it in a state where the user can understand the progress?
|
||||||
|
|
||||||
|
## Effects of This Skill
|
||||||
|
|
||||||
|
When this skill is used correctly:
|
||||||
|
|
||||||
|
- ✅ tasklist.md always reflects the latest progress
|
||||||
|
- ✅ The user can grasp progress at a glance
|
||||||
|
- ✅ Divergence between the document and the implementation disappears
|
||||||
|
- ✅ Retrospectives become easy, leading to improvements next time
|
||||||
|
- ✅ A record valuable as project history remains
|
||||||
|
|
||||||
|
## Important Reminder
|
||||||
|
|
||||||
|
🚨 **The most important role of this skill is to reliably manage the progress of tasklist.md.**
|
||||||
|
|
||||||
|
- todowrite is a volatile note (not visible to the user)
|
||||||
|
- **tasklist.md is the persistent document (the user sees it)**
|
||||||
|
|
||||||
|
During implementation, always ask yourself, "When the user looks at tasklist.md, can they understand the progress?"
|
||||||
96
.opencode/skills/steering/templates/design.md
Normal file
96
.opencode/skills/steering/templates/design.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Design Document
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
{Describe the architecture pattern to adopt}
|
||||||
|
|
||||||
|
```
|
||||||
|
{Describe the architecture diagram in text or Mermaid}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Design
|
||||||
|
|
||||||
|
### 1. {Component name 1}
|
||||||
|
|
||||||
|
**Responsibilities**:
|
||||||
|
- {Responsibility 1}
|
||||||
|
- {Responsibility 2}
|
||||||
|
|
||||||
|
**Implementation highlights**:
|
||||||
|
- {Points to note during implementation}
|
||||||
|
- {Technical constraints}
|
||||||
|
|
||||||
|
### 2. {Component name 2}
|
||||||
|
|
||||||
|
**Responsibilities**:
|
||||||
|
- {Responsibility 1}
|
||||||
|
- {Responsibility 2}
|
||||||
|
|
||||||
|
**Implementation highlights**:
|
||||||
|
- {Points to note during implementation}
|
||||||
|
- {Technical constraints}
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### {Use case name}
|
||||||
|
```
|
||||||
|
1. {Step 1}
|
||||||
|
2. {Step 2}
|
||||||
|
3. {Step 3}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling Strategy
|
||||||
|
|
||||||
|
### Custom Error Classes
|
||||||
|
|
||||||
|
{Define the necessary error classes}
|
||||||
|
|
||||||
|
### Error Handling Patterns
|
||||||
|
|
||||||
|
{How errors are handled}
|
||||||
|
|
||||||
|
## Test Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- {Test target 1}
|
||||||
|
- {Test target 2}
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- {Test scenario 1}
|
||||||
|
- {Test scenario 2}
|
||||||
|
|
||||||
|
## Dependent Libraries
|
||||||
|
|
||||||
|
{Describe any newly added libraries}
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"{library name}": "{version}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
{File structure to be added or changed}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Order
|
||||||
|
|
||||||
|
1. {Implementation step 1}
|
||||||
|
2. {Implementation step 2}
|
||||||
|
3. {Implementation step 3}
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- {Security-related points to note}
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
- {Performance-related points to note}
|
||||||
|
|
||||||
|
## Future Extensibility
|
||||||
|
|
||||||
|
{A design that accounts for future extensions}
|
||||||
48
.opencode/skills/steering/templates/requirements.md
Normal file
48
.opencode/skills/steering/templates/requirements.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Requirements
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
{Describe the feature overview in 1-2 sentences}
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
{Why this feature is needed and what problem it solves}
|
||||||
|
|
||||||
|
## Features to Implement
|
||||||
|
|
||||||
|
### 1. {Feature name 1}
|
||||||
|
- {Specific feature description}
|
||||||
|
- {What the user will be able to do}
|
||||||
|
|
||||||
|
### 2. {Feature name 2}
|
||||||
|
- {Specific feature description}
|
||||||
|
- {What the user will be able to do}
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
### {Feature name 1}
|
||||||
|
- [ ] {Verifiable condition 1}
|
||||||
|
- [ ] {Verifiable condition 2}
|
||||||
|
- [ ] {Verifiable condition 3}
|
||||||
|
|
||||||
|
### {Feature name 2}
|
||||||
|
- [ ] {Verifiable condition 1}
|
||||||
|
- [ ] {Verifiable condition 2}
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
- {Describe quantitative goals if any}
|
||||||
|
- {Describe qualitative goals if any}
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
The following will not be implemented in this phase:
|
||||||
|
|
||||||
|
- {Feature not implemented 1}
|
||||||
|
- {Feature not implemented 2}
|
||||||
|
|
||||||
|
## Reference Documents
|
||||||
|
|
||||||
|
- `docs/product-requirements.md` - Product Requirements Document
|
||||||
|
- `docs/functional-design.md` - Functional design document
|
||||||
|
- `docs/architecture.md` - Architecture design document
|
||||||
107
.opencode/skills/steering/templates/tasklist.md
Normal file
107
.opencode/skills/steering/templates/tasklist.md
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# Task List
|
||||||
|
|
||||||
|
## 🚨 Principle of Fully Completing Tasks
|
||||||
|
|
||||||
|
**Keep working until all tasks in this file are complete**
|
||||||
|
|
||||||
|
### Mandatory Rules
|
||||||
|
- **Make every task `[x]`**
|
||||||
|
- "Planned as a separate task due to time constraints" is forbidden
|
||||||
|
- "Postponed because the implementation is too complex" is forbidden
|
||||||
|
- Do not finish work while leaving incomplete tasks (`[ ]`)
|
||||||
|
|
||||||
|
### Plan Only Tasks That Can Be Implemented
|
||||||
|
- At the planning stage, list only "tasks that can be implemented"
|
||||||
|
- Do not include "tasks that might be done in the future"
|
||||||
|
- Do not include "tasks under consideration"
|
||||||
|
|
||||||
|
### The Only Case Where Skipping a Task Is Permitted
|
||||||
|
Skipping is possible only when one of the following technical reasons applies:
|
||||||
|
- A change in the implementation approach made the feature itself unnecessary
|
||||||
|
- An architecture change replaced it with a different implementation method
|
||||||
|
- A change in dependencies made the task impossible to execute
|
||||||
|
|
||||||
|
When skipping, always state the reason clearly:
|
||||||
|
```markdown
|
||||||
|
- [x] ~~task name~~ (unnecessary due to a change in approach: specific technical reason)
|
||||||
|
```
|
||||||
|
|
||||||
|
### If a Task Is Too Large
|
||||||
|
- Split the task into smaller subtasks
|
||||||
|
- Add the split subtasks to this file
|
||||||
|
- Complete the subtasks one by one
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: {Phase name}
|
||||||
|
|
||||||
|
- [ ] {Task 1}
|
||||||
|
- [ ] {Subtask 1-1}
|
||||||
|
- [ ] {Subtask 1-2}
|
||||||
|
|
||||||
|
- [ ] {Task 2}
|
||||||
|
- [ ] {Subtask 2-1}
|
||||||
|
- [ ] {Subtask 2-2}
|
||||||
|
|
||||||
|
## Phase 2: {Phase name}
|
||||||
|
|
||||||
|
- [ ] {Task 1}
|
||||||
|
- [ ] {Subtask 1-1}
|
||||||
|
- [ ] {Subtask 1-2}
|
||||||
|
|
||||||
|
- [ ] {Task 2}
|
||||||
|
|
||||||
|
## Phase 3: Quality Check and Fixes
|
||||||
|
|
||||||
|
- [ ] Confirm that all tests pass
|
||||||
|
- [ ] `npm test`
|
||||||
|
- [ ] Confirm that there are no lint errors
|
||||||
|
- [ ] `npm run lint`
|
||||||
|
- [ ] Confirm that there are no type errors
|
||||||
|
- [ ] `npm run typecheck`
|
||||||
|
- [ ] Confirm that the build succeeds
|
||||||
|
- [ ] `npm run build`
|
||||||
|
|
||||||
|
## Phase 4: Documentation Updates
|
||||||
|
|
||||||
|
- [ ] Update README.md (as needed)
|
||||||
|
- [ ] Post-implementation retrospective (record at the bottom of this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post-Implementation Retrospective
|
||||||
|
|
||||||
|
### Implementation Completion Date
|
||||||
|
{YYYY-MM-DD}
|
||||||
|
|
||||||
|
### Differences Between Plan and Actual
|
||||||
|
|
||||||
|
**Points that differed from the plan**:
|
||||||
|
- {Technical changes not anticipated at planning time}
|
||||||
|
- {Changes in the implementation approach and the reasons}
|
||||||
|
|
||||||
|
**Tasks that became newly necessary**:
|
||||||
|
- {Tasks added during implementation}
|
||||||
|
- {Why the addition was necessary}
|
||||||
|
|
||||||
|
**Tasks skipped for technical reasons** (only when applicable):
|
||||||
|
- {Task name}
|
||||||
|
- Reason for skipping: {specific technical reason}
|
||||||
|
- Alternative implementation: {what it was replaced with}
|
||||||
|
|
||||||
|
**⚠️ Note**: Do not list tasks skipped for reasons such as "time constraints" or "difficulty" here. Completing all tasks is the principle.
|
||||||
|
|
||||||
|
### Lessons Learned
|
||||||
|
|
||||||
|
**Technical insights**:
|
||||||
|
- {Technical knowledge gained through implementation}
|
||||||
|
- {New technologies or patterns used}
|
||||||
|
|
||||||
|
**Process improvements**:
|
||||||
|
- {What went well in task management}
|
||||||
|
- {How the steering files were leveraged}
|
||||||
|
|
||||||
|
### Improvement Suggestions for Next Time
|
||||||
|
- {Things to watch out for in the next feature addition}
|
||||||
|
- {More efficient implementation methods}
|
||||||
|
- {Improvements to task planning}
|
||||||
14
.prettierignore
Normal file
14
.prettierignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# ドキュメント・設定ディレクトリ
|
||||||
|
.opencode/
|
||||||
|
.steering/
|
||||||
|
docs/
|
||||||
|
AGENTS.md
|
||||||
|
|
||||||
|
# ログファイル
|
||||||
|
*log.json
|
||||||
|
|
||||||
|
# 依存関係
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# ビルド成果物
|
||||||
|
dist/
|
||||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
||||||
0
.steering/.gitkeep
Normal file
0
.steering/.gitkeep
Normal file
134
AGENTS.md
Normal file
134
AGENTS.md
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# Project Memory
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
- Development environment: devcontainer
|
||||||
|
- Node.js v24.11.0
|
||||||
|
- TypeScript 5.x
|
||||||
|
- Package manager: npm
|
||||||
|
|
||||||
|
## Basic Principles of Spec-Driven Development
|
||||||
|
|
||||||
|
### Basic Flow
|
||||||
|
|
||||||
|
1. **Document creation**: Define "what to build" in persistent documents (`docs/`)
|
||||||
|
2. **Work planning**: Plan "what to do this time" in steering files (`.steering/`)
|
||||||
|
3. **Implementation**: Implement according to tasklist.md and update progress as you go
|
||||||
|
4. **Verification**: Testing and operation checks
|
||||||
|
5. **Update**: Update documents as needed
|
||||||
|
|
||||||
|
### Important Rules
|
||||||
|
|
||||||
|
#### When Creating Documents
|
||||||
|
|
||||||
|
**Create one file at a time, and always obtain the user's approval before moving on to the next.**
|
||||||
|
|
||||||
|
When waiting for approval, communicate clearly:
|
||||||
|
```
|
||||||
|
"The creation of [document name] is complete. Please review the content.
|
||||||
|
Once you approve, I will proceed to the next document."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Checks Before Implementation
|
||||||
|
|
||||||
|
Before starting a new implementation, always check the following:
|
||||||
|
|
||||||
|
1. Read AGENTS.md
|
||||||
|
2. Read the related persistent documents (`docs/`)
|
||||||
|
3. Search for existing similar implementations with Grep
|
||||||
|
4. Understand existing patterns before starting implementation
|
||||||
|
|
||||||
|
#### Steering File Management
|
||||||
|
|
||||||
|
Create `.steering/[YYYYMMDD]-[task-name]/` for each piece of work:
|
||||||
|
|
||||||
|
- `requirements.md`: The requirements for this work
|
||||||
|
- `design.md`: The implementation approach
|
||||||
|
- `tasklist.md`: A concrete task list
|
||||||
|
|
||||||
|
Naming convention: `20250115-add-user-profile` format
|
||||||
|
|
||||||
|
#### Steering File Management
|
||||||
|
|
||||||
|
**Use the `steering` skill when planning work, implementing, and verifying.**
|
||||||
|
|
||||||
|
- **When planning work**: Mode 1 (creating steering files) via the steering skill
|
||||||
|
- **When implementing**: Mode 2 (implementation and tasklist.md update management) via the steering skill
|
||||||
|
- **When verifying**: Mode 3 (retrospective) via the steering skill
|
||||||
|
|
||||||
|
Detailed procedures and update-management rules are defined within the steering skill.
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
### Persistent Documents (`docs/`)
|
||||||
|
|
||||||
|
Define "what to build" and "how to build it" for the entire application:
|
||||||
|
|
||||||
|
#### Drafts and Ideas (`docs/ideas/`)
|
||||||
|
- Outputs of brainstorming and ideation
|
||||||
|
- Technical research notes
|
||||||
|
- Free-form (minimal structure)
|
||||||
|
- Automatically loaded when `/setup-project` is run
|
||||||
|
|
||||||
|
#### Official Documents
|
||||||
|
- **product-requirements.md** - Product requirements document
|
||||||
|
- **functional-design.md** - Functional design document
|
||||||
|
- **architecture.md** - Technical specification
|
||||||
|
- **repository-structure.md** - Repository structure definition document
|
||||||
|
- **development-guidelines.md** - Development guidelines
|
||||||
|
- **glossary.md** - Ubiquitous language definitions
|
||||||
|
|
||||||
|
### Work-Unit Documents (`.steering/`)
|
||||||
|
|
||||||
|
Define "what to do this time" for a specific development task:
|
||||||
|
|
||||||
|
- `requirements.md`: The requirements for this work
|
||||||
|
- `design.md`: The design of the changes
|
||||||
|
- `tasklist.md`: The task list
|
||||||
|
|
||||||
|
### opencode Configuration (`.opencode/`)
|
||||||
|
|
||||||
|
- `.opencode/agent/` - Subagent definitions (doc-reviewer, implementation-validator)
|
||||||
|
- `.opencode/command/` - Slash commands (setup-project, add-feature, review-docs)
|
||||||
|
- `.opencode/skills/` - Task-specific skills (prd-writing, functional-design, architecture-design, repository-structure, development-guidelines, glossary-creation, steering)
|
||||||
|
|
||||||
|
## Development Process
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
1. Use this template
|
||||||
|
2. Create persistent documents with `/setup-project` (interactively creating six)
|
||||||
|
3. Implement features with `/add-feature [feature]`
|
||||||
|
|
||||||
|
### Day-to-Day Usage
|
||||||
|
|
||||||
|
**Basically, just make requests through normal conversation:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Editing documents
|
||||||
|
> Please add a new feature to the PRD
|
||||||
|
> Review the performance requirements in architecture.md
|
||||||
|
> Add a new domain term to glossary.md
|
||||||
|
|
||||||
|
# Adding features (use commands for the standard flow)
|
||||||
|
> /add-feature edit user profile
|
||||||
|
|
||||||
|
# Detailed review (when a detailed report is needed)
|
||||||
|
> /review-docs docs/product-requirements.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key point**: You do not need to be conscious of the details of spec-driven development. opencode will determine and load the appropriate skill.
|
||||||
|
|
||||||
|
## Principles of Document Management
|
||||||
|
|
||||||
|
### Persistent Documents (`docs/`)
|
||||||
|
|
||||||
|
- Describe the fundamental design
|
||||||
|
- Not updated frequently
|
||||||
|
- The "north star" for the entire project
|
||||||
|
|
||||||
|
### Work-Unit Documents (`.steering/`)
|
||||||
|
|
||||||
|
- Specialized for a specific task
|
||||||
|
- Created anew for each piece of work
|
||||||
|
- Retained as history
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Ken Yasue
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
399
README.md
Normal file
399
README.md
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
# opencode Spec-Driven Development Boilerplate
|
||||||
|
|
||||||
|
A spec-driven development boilerplate for [opencode](https://opencode.ai), converted from the
|
||||||
|
[Claude Code boilerplate](https://git.yasue.org/ken/claudecode-boilerplate) that accompanies the book
|
||||||
|
*"Practical Claude Code Introduction."*
|
||||||
|
|
||||||
|
It gives opencode a structured workflow for turning ideas into production code through persistent design
|
||||||
|
documents, per-task steering files, AI skills, subagents, and slash commands.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [What Is Spec-Driven Development?](#what-is-spec-driven-development)
|
||||||
|
- [Conversion Summary (Claude Code → opencode)](#conversion-summary-claude-code--opencode)
|
||||||
|
- [Prerequisites](#prerequisites)
|
||||||
|
- [Quick Start](#quick-start)
|
||||||
|
- [Directory Structure](#directory-structure)
|
||||||
|
- [Configuration Overview](#configuration-overview)
|
||||||
|
- [Usage Workflow](#usage-workflow)
|
||||||
|
- [Commands Reference](#commands-reference)
|
||||||
|
- [Skills Reference](#skills-reference)
|
||||||
|
- [Agents Reference](#agents-reference)
|
||||||
|
- [Customizing the Boilerplate](#customizing-the-boilerplate)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Is Spec-Driven Development?
|
||||||
|
|
||||||
|
Spec-driven development separates **"what to build"** from **"how to build it"**:
|
||||||
|
|
||||||
|
1. **Persistent documents** (`docs/`) — the north-star design (PRD, functional design, architecture, etc.)
|
||||||
|
2. **Steering files** (`.steering/`) — per-task plans created fresh for each piece of work
|
||||||
|
3. **Implementation** — the agent follows `tasklist.md`, updating progress as it goes
|
||||||
|
4. **Verification** — tests, lint, typecheck, and a retrospective
|
||||||
|
|
||||||
|
opencode loads the right skill automatically based on what you're doing, so you don't have to think about
|
||||||
|
the process — just describe what you want.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conversion Summary (Claude Code → opencode)
|
||||||
|
|
||||||
|
| Claude Code | opencode | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `CLAUDE.md` | `AGENTS.md` | Referenced via `instructions` in `opencode.json` |
|
||||||
|
| `.claude/settings.json` | `opencode.json` | Permissions converted to opencode's `permission` schema |
|
||||||
|
| `.claude/agents/*.md` | `.opencode/agent/*.md` | Added `mode: subagent`; `model: sonnet` removed (inherits default) |
|
||||||
|
| `.claude/commands/*.md` | `.opencode/command/*.md` | Tool-call syntax (`Bash()`, `Grep()`, `Skill()`) converted to natural instructions; `$ARGUMENTS` added |
|
||||||
|
| `.claude/skills/*/SKILL.md` | `.opencode/skills/*/SKILL.md` | `allowed-tools` frontmatter removed (not an opencode field) |
|
||||||
|
| `Skill('steering')` calls | Auto-loaded by description | opencode surfaces skills via `description` matching |
|
||||||
|
| `TodoWrite` / `Edit` tool refs | `todowrite` / `edit` | Lowercased to match opencode tool names |
|
||||||
|
| Claude Code devcontainer feature | `curl … opencode.ai/install` | Replaced Anthropic feature with opencode install script |
|
||||||
|
|
||||||
|
All skill templates and guides were preserved verbatim except for `.claude/` path references and
|
||||||
|
"Claude Code" mentions, which were updated to `.opencode/` and "opencode" respectively.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Node.js** v18+ (v24.11.0 recommended)
|
||||||
|
- **npm**
|
||||||
|
- **opencode** — install with:
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://opencode.ai/install | bash
|
||||||
|
```
|
||||||
|
See <https://opencode.ai/docs> for alternatives (npm, Homebrew, etc.)
|
||||||
|
- **Docker** (only if using the Dev Container)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Option A — Dev Container (recommended)
|
||||||
|
|
||||||
|
1. Open this folder in **VS Code**.
|
||||||
|
2. When prompted, click **"Reopen in Container"** (or run the command
|
||||||
|
*Dev Containers: Reopen in Container*).
|
||||||
|
3. The container automatically:
|
||||||
|
- Installs Node.js LTS
|
||||||
|
- Runs `npm install`
|
||||||
|
- Installs opencode
|
||||||
|
4. Open a terminal and start opencode:
|
||||||
|
```bash
|
||||||
|
opencode
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B — Manual setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 2. Set up Git hooks
|
||||||
|
npm run prepare
|
||||||
|
|
||||||
|
# 3. Start opencode
|
||||||
|
opencode
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── opencode.json # opencode config (permissions, instructions)
|
||||||
|
├── AGENTS.md # Project instructions (loaded into every session)
|
||||||
|
├── package.json # Node.js project + scripts
|
||||||
|
├── tsconfig.json # TypeScript config
|
||||||
|
├── eslint.config.js # ESLint flat config
|
||||||
|
├── vitest.config.ts # Vitest config (80% coverage threshold)
|
||||||
|
├── .prettierrc / .prettierignore
|
||||||
|
├── .gitignore
|
||||||
|
├── .husky/pre-commit # Runs lint-staged + typecheck before commit
|
||||||
|
├── .devcontainer/devcontainer.json
|
||||||
|
├── prompt.md # Example MVP prompt
|
||||||
|
│
|
||||||
|
├── src/ # Source code
|
||||||
|
│ ├── example.ts
|
||||||
|
│ └── example.test.ts
|
||||||
|
│
|
||||||
|
├── docs/ # Persistent design documents (created by /setup-project)
|
||||||
|
│ └── ideas/
|
||||||
|
│ └── initial-requirements.md # Brainstorming notes (pre-PRD)
|
||||||
|
│
|
||||||
|
├── .steering/ # Per-task steering files (created by /add-feature)
|
||||||
|
│ └── .gitkeep
|
||||||
|
│
|
||||||
|
└── .opencode/ # opencode configuration
|
||||||
|
├── agent/ # Subagent definitions
|
||||||
|
│ ├── doc-reviewer.md
|
||||||
|
│ └── implementation-validator.md
|
||||||
|
├── command/ # Slash commands
|
||||||
|
│ ├── setup-project.md
|
||||||
|
│ ├── add-feature.md
|
||||||
|
│ └── review-docs.md
|
||||||
|
└── skills/ # Task-specific skills
|
||||||
|
├── prd-writing/ (SKILL.md + template.md)
|
||||||
|
├── functional-design/ (SKILL.md + template.md + guide.md)
|
||||||
|
├── architecture-design/ (SKILL.md + template.md + guide.md)
|
||||||
|
├── repository-structure/ (SKILL.md + template.md + guide.md)
|
||||||
|
├── development-guidelines/(SKILL.md + template.md + guides/)
|
||||||
|
├── glossary-creation/ (SKILL.md + template.md + guide.md)
|
||||||
|
└── steering/ (SKILL.md + templates/)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Overview
|
||||||
|
|
||||||
|
### `opencode.json`
|
||||||
|
|
||||||
|
The main configuration file. Key sections:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"instructions": ["AGENTS.md"], // Loaded into every session
|
||||||
|
"permission": {
|
||||||
|
"skill": "allow", // Skills are auto-available
|
||||||
|
"bash": { // Common dev commands pre-approved
|
||||||
|
"*": "ask",
|
||||||
|
"npm *": "allow",
|
||||||
|
"npx *": "allow",
|
||||||
|
"git *": "allow",
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **After editing `opencode.json`, quit and restart opencode** — config is loaded once at startup.
|
||||||
|
|
||||||
|
### `AGENTS.md`
|
||||||
|
|
||||||
|
The project memory file. opencode reads it on every session. It defines:
|
||||||
|
- The technology stack
|
||||||
|
- The spec-driven development principles
|
||||||
|
- The directory structure
|
||||||
|
- The development process and workflow
|
||||||
|
|
||||||
|
### Agents (`.opencode/agent/`)
|
||||||
|
|
||||||
|
Subagents run in isolated contexts via the `task` tool. They don't consume the main session's context.
|
||||||
|
|
||||||
|
### Commands (`.opencode/command/`)
|
||||||
|
|
||||||
|
Slash commands invoked in the opencode TUI with `/command-name`. Each command is a prompt template
|
||||||
|
that opencode executes.
|
||||||
|
|
||||||
|
### Skills (`.opencode/skills/`)
|
||||||
|
|
||||||
|
Skills are loaded automatically based on their `description` field. You don't invoke them by name —
|
||||||
|
opencode surfaces the right skill when the task matches.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage Workflow
|
||||||
|
|
||||||
|
### 1. Initial project setup
|
||||||
|
|
||||||
|
Run the setup command to create all six persistent design documents interactively:
|
||||||
|
|
||||||
|
```
|
||||||
|
> /setup-project
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates (one at a time, with your approval):
|
||||||
|
1. `docs/product-requirements.md` (PRD)
|
||||||
|
2. `docs/functional-design.md`
|
||||||
|
3. `docs/architecture.md`
|
||||||
|
4. `docs/repository-structure.md`
|
||||||
|
5. `docs/development-guidelines.md`
|
||||||
|
6. `docs/glossary.md`
|
||||||
|
|
||||||
|
The PRD is based on the brainstorming notes in `docs/ideas/initial-requirements.md`. Edit that file
|
||||||
|
first if you have your own idea, or replace it.
|
||||||
|
|
||||||
|
### 2. Add a feature
|
||||||
|
|
||||||
|
```
|
||||||
|
> /add-feature User authentication
|
||||||
|
```
|
||||||
|
|
||||||
|
This fully autonomous workflow:
|
||||||
|
1. Creates `.steering/[YYYYMMDD]-[feature-name]/` with `requirements.md`, `design.md`, `tasklist.md`
|
||||||
|
2. Generates the steering files (planning mode of the steering skill)
|
||||||
|
3. Implements every task in `tasklist.md`, marking each `[ ]` → `[x]` as it goes
|
||||||
|
4. Launches the `implementation-validator` subagent for quality review
|
||||||
|
5. Runs `npm test`, `npm run lint`, `npm run typecheck`
|
||||||
|
6. Records a retrospective in `tasklist.md`
|
||||||
|
|
||||||
|
### 3. Review a document
|
||||||
|
|
||||||
|
```
|
||||||
|
> /review-docs docs/product-requirements.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Launches the `doc-reviewer` subagent to evaluate the document from five perspectives:
|
||||||
|
completeness, clarity, consistency, implementability, and measurability.
|
||||||
|
|
||||||
|
### 4. Day-to-day editing
|
||||||
|
|
||||||
|
You don't always need a command — just ask in normal conversation:
|
||||||
|
|
||||||
|
```
|
||||||
|
> Add a new feature to the PRD
|
||||||
|
> Review the performance requirements in architecture.md
|
||||||
|
> Add a new domain term to glossary.md
|
||||||
|
```
|
||||||
|
|
||||||
|
opencode loads the appropriate skill automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commands Reference
|
||||||
|
|
||||||
|
| Command | Description | Example |
|
||||||
|
|---|---|---|
|
||||||
|
| `/setup-project` | Create the six persistent documents interactively | `/setup-project` |
|
||||||
|
| `/add-feature` | Implement a feature end-to-end (autonomous) | `/add-feature User profile editing` |
|
||||||
|
| `/review-docs` | Detailed document review via subagent | `/review-docs docs/architecture.md` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Skills Reference
|
||||||
|
|
||||||
|
| Skill | When it loads | Output |
|
||||||
|
|---|---|---|
|
||||||
|
| `prd-writing` | Creating a Product Requirements Document | `docs/product-requirements.md` |
|
||||||
|
| `functional-design` | Creating a functional design document | `docs/functional-design.md` |
|
||||||
|
| `architecture-design` | Designing the system architecture | `docs/architecture.md` |
|
||||||
|
| `repository-structure` | Defining the directory layout | `docs/repository-structure.md` |
|
||||||
|
| `development-guidelines` | Creating coding conventions / implementing code | `docs/development-guidelines.md` |
|
||||||
|
| `glossary-creation` | Creating a project glossary | `docs/glossary.md` |
|
||||||
|
| `steering` | Planning, implementing, and retrospecting on a task | `.steering/[date]-[name]/` files |
|
||||||
|
|
||||||
|
Each skill folder contains a `SKILL.md` (instructions) plus `template.md` and/or `guide.md` (reference
|
||||||
|
material the skill points to).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agents Reference
|
||||||
|
|
||||||
|
| Agent | Mode | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `doc-reviewer` | subagent | Reviews document quality across 5 dimensions; outputs a scored report with prioritized improvements |
|
||||||
|
| `implementation-validator` | subagent | Validates code against the spec; checks quality, test coverage, security, performance; runs lint/test/typecheck |
|
||||||
|
|
||||||
|
Agents are launched via the `task` tool (e.g., by the `/add-feature` and `/review-docs` commands).
|
||||||
|
They run in an isolated context window.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Customizing the Boilerplate
|
||||||
|
|
||||||
|
### Change the model
|
||||||
|
|
||||||
|
Agents inherit the default model from `opencode.json`. To pin a specific model for a subagent, add
|
||||||
|
`model` to its frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
description: ...
|
||||||
|
mode: subagent
|
||||||
|
model: anthropic/claude-sonnet-4-6
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Or set a global default in `opencode.json`:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"model": "anthropic/claude-sonnet-4-6"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> Model IDs use the `provider/model-id` format. See <https://opencode.ai/config.json> for the full schema.
|
||||||
|
|
||||||
|
### Adjust permissions
|
||||||
|
|
||||||
|
Edit the `permission` block in `opencode.json`. For example, to allow all bash commands:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"permission": { "bash": "allow" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add a new skill
|
||||||
|
|
||||||
|
1. Create `.opencode/skills/my-skill/SKILL.md`
|
||||||
|
2. Add frontmatter with `name` and `description` (the description controls when opencode loads it)
|
||||||
|
3. Add any companion `template.md` or `guide.md` files
|
||||||
|
4. Restart opencode
|
||||||
|
|
||||||
|
### Add a new command
|
||||||
|
|
||||||
|
1. Create `.opencode/command/my-command.md`
|
||||||
|
2. Add `description` frontmatter + a prompt body (use `$ARGUMENTS` for user input)
|
||||||
|
3. Restart opencode
|
||||||
|
|
||||||
|
### Add a new agent
|
||||||
|
|
||||||
|
1. Create `.opencode/agent/my-agent.md`
|
||||||
|
2. Add `description` and `mode: subagent` frontmatter + a prompt body
|
||||||
|
3. Restart opencode
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### opencode won't start after editing config
|
||||||
|
|
||||||
|
opencode validates `opencode.json` strictly and refuses to start on invalid fields. To recover:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Skip the project config and start from globals only
|
||||||
|
OPENCODE_DISABLE_PROJECT_CONFIG=1 opencode
|
||||||
|
```
|
||||||
|
|
||||||
|
Then fix the broken file and restart normally.
|
||||||
|
|
||||||
|
### Skills not loading
|
||||||
|
|
||||||
|
- Ensure each `SKILL.md` has a `description` in its frontmatter — skills without one are filtered out.
|
||||||
|
- Ensure the folder name matches the `name` field.
|
||||||
|
- Restart opencode after adding or changing skills.
|
||||||
|
|
||||||
|
### Commands not appearing
|
||||||
|
|
||||||
|
- Command files must be directly inside `.opencode/command/` (not in subdirectories).
|
||||||
|
- Restart opencode after adding commands.
|
||||||
|
|
||||||
|
### Dev Container: `opencode` command not found
|
||||||
|
|
||||||
|
The install script adds opencode to `~/.opencode/bin/` and updates your shell profile. If the command
|
||||||
|
isn't found in a new terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH="$HOME/.opencode/bin:$PATH"
|
||||||
|
opencode
|
||||||
|
```
|
||||||
|
|
||||||
|
Or create a permanent symlink:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ln -sf "$HOME/.opencode/bin/opencode" /usr/local/bin/opencode
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT — see [LICENSE](LICENSE).
|
||||||
271
docs/architecture.md
Normal file
271
docs/architecture.md
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
# アーキテクチャ設計書 (Architecture Design Document)
|
||||||
|
|
||||||
|
> 本書は `docs/product-requirements.md` と `docs/functional-design.md` を技術的に実現するためのシステム構造・技術選定・インフラ要件を定義する。
|
||||||
|
|
||||||
|
## 技術スタック
|
||||||
|
|
||||||
|
### 言語・ランタイム
|
||||||
|
|
||||||
|
| 技術 | バージョン | 選定理由 |
|
||||||
|
|------|-----------|----------|
|
||||||
|
| Node.js | v24.11.0 | 開発環境(devcontainer)と同一。better-sqlite3の同期APIを直接扱える。LTS保証下で本番運用が安定 |
|
||||||
|
| TypeScript | 5.x | 静的型付けによりコンパイル時バグ検出。Repository/Service/Entity間の型共有で保守性向上。IDE補完により開発効率向上 |
|
||||||
|
| npm | 11.x | Node.js v24.11.0にバンドル。package-lock.jsonによる厳密な依存管理。workspaces対応 |
|
||||||
|
|
||||||
|
### フレームワーク・ライブラリ
|
||||||
|
|
||||||
|
| 技術 | バージョン | 目的 | 選定理由 |
|
||||||
|
|------|-----------|------|----------|
|
||||||
|
| Next.js | 15 | Webフレームワーク | App Router・Server Components・Route Handlers・Server Actionsを統合利用。Node.js Runtime選択可能でSQLite直接操作に適合 |
|
||||||
|
| better-sqlite3 | 最新安定 | SQLiteドライバ | 同期APIで扱いやすく高速。トランザクション・プリペアドステートメント・pragma制御が直接可能 |
|
||||||
|
| react-markdown | 最新安定 | Markdownレンダリング | GFM拡張と連携可能。プラグインでサニタイズパイプラインを構築できる |
|
||||||
|
| remark-gfm | 最新安定 | GFM対応 | テーブル・チェックリスト・取り消し線等のGFM記法を有効化 |
|
||||||
|
| rehype-sanitize | 最新安定 | HTMLサニタイズ | Markdown表示時のXSS対策。危険なHTML・URLスキームを除去 |
|
||||||
|
| Tailwind CSS | 最新安定 | スタイリング | ユーティリティファーストでUI構築が高速。カスタムCSS削減 |
|
||||||
|
| bcrypt | 最新安定 | パスワードハッシュ | ソルト付きハッシュで平文保存を回避。適切なコスト係数でブルートフォース耐性 |
|
||||||
|
| FullCalendar系 または独自実装 | - | カレンダーUI | 月/週/日/リスト表示・ドラッグ操作・イベントフィルタ要件を満たす |
|
||||||
|
|
||||||
|
### 開発ツール
|
||||||
|
|
||||||
|
| 技術 | バージョン | 目的 | 選定理由 |
|
||||||
|
|------|-----------|------|----------|
|
||||||
|
| Vitest | 最新安定 | Unit Test | Viteベースで高速。TypeScriptネイティブ対応。better-sqlite3等のネイティブモジュールも扱える |
|
||||||
|
| Playwright | 最新安定 | E2E Test | 実ブラウザ(Chromium/Firefox/WebKit)操作で主要ユーザーフローを検証。SSEのリアルタイム挙動も検証可能 |
|
||||||
|
| ESLint | 最新安定 | リンタ | コード品質・一貫性維持。TypeScriptルールと連携 |
|
||||||
|
| TypeScript(tsc) | 5.x | 型チェック | 型エラーをCIで検出 |
|
||||||
|
|
||||||
|
## アーキテクチャパターン
|
||||||
|
|
||||||
|
### レイヤードアーキテクチャ
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ UI層 (Next.js) │ ← Server Components / Client Components
|
||||||
|
│ Route Handler / Server Action / SSE │ 入力受付・表示・認証・認可
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Service層 │ ← 業務ロジック・権限チェック・トランザクション
|
||||||
|
│ AuthService / ProjectService / ... │ 通知生成・アクティビティログ・SSE配信
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Repository層 │ ← SQL保持・データアクセス・論理削除フィルタ
|
||||||
|
│ UserRepository / ProjectRepository / ... │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Data層 │ ← SQLite接続・SQL実行・Migration
|
||||||
|
│ lib/db/sqlite.ts / migrator.ts / SQLite │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### レイヤ責務と依存規則
|
||||||
|
|
||||||
|
依存は一方向(UI → Service → Repository → Data)のみ許可する。逆方向依存・層飛ばしを禁止する。
|
||||||
|
|
||||||
|
#### UI層(Route Handler / Server Action / Server Components / SSEエンドポイント)
|
||||||
|
- **責務**: 入力受付・バリデーション・認証・認可・結果表示
|
||||||
|
- **許可操作**: Service層の呼び出し
|
||||||
|
- **禁止操作**: Repository層・Data層への直接アクセス、SQLの直接記述
|
||||||
|
|
||||||
|
#### Service層
|
||||||
|
- **責務**: 業務ロジック・権限チェック(`isMember`/ロール)・トランザクション境界・副作用(通知生成・アクティビティログ記録・SSE配信)
|
||||||
|
- **許可操作**: Repository層の呼び出し・FileStorageServiceによるファイル操作・SseHubへの配信依頼
|
||||||
|
- **禁止操作**: UI層への依存、SQLの直接記述、SQLiteライブラリの直接操作
|
||||||
|
|
||||||
|
#### Repository層
|
||||||
|
- **責務**: SQLの保持・パラメータバインド実行・論理削除(`deleted_at IS NULL`)の確実な付与・プロジェクト分離の担保
|
||||||
|
- **許可操作**: SQLラッパー(`lib/db/sqlite.ts`)経由のDBアクセス
|
||||||
|
- **禁止操作**: 業務ロジックの実装、SQLiteライブラリの直接操作、UI層への依存
|
||||||
|
|
||||||
|
#### Data層
|
||||||
|
- **責務**: SQLite接続の共通管理・SQL実行・トランザクション・Migration
|
||||||
|
- **許可操作**: better-sqlite3へのアクセス・ファイルシステム(Migrationファイル・バックアップ)へのアクセス
|
||||||
|
- **禁止操作**: 業務ロジックの実装
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 許容: UI → Service → Repository → Data
|
||||||
|
routeHandler → projectService.createProject(...) → projectRepository.create(...) → db.execute(sql, params)
|
||||||
|
|
||||||
|
// 禁止: UI → Data (層飛ばし)
|
||||||
|
routeHandler → db.execute("INSERT INTO projects ...") // ❌
|
||||||
|
|
||||||
|
// 禁止: Repository → SQLite直接 (ラッパー未使用)
|
||||||
|
projectRepository → new Database(dbPath) // ❌
|
||||||
|
```
|
||||||
|
|
||||||
|
### ランタイム方針
|
||||||
|
|
||||||
|
SQLiteを直接扱うため、Next.jsのEdge Runtimeは使用しない。API Route・Server Actions・DBアクセス処理・SSEエンドポイントはすべてNode.js Runtimeで実行する。各Route Handlerの先頭で `export const runtime = 'nodejs'` を明示する。
|
||||||
|
|
||||||
|
### Prisma不使用の方針
|
||||||
|
|
||||||
|
DBアクセスは独自SQLラッパー(`lib/db/sqlite.ts`)とRepositoryクラスで実装する。Prismaは導入しない。
|
||||||
|
- 理由1: SQLを直接制御することでクエリ最適化・インデックス設計を明示できる
|
||||||
|
- 理由2: better-sqlite3の同期APIと相性が良く、コード生成のオーバーヘッドがない
|
||||||
|
- 理由3: MigrationもSQLファイルベースの独自実装でバージョン管理する
|
||||||
|
|
||||||
|
## データ永続化戦略
|
||||||
|
|
||||||
|
### 保存方式
|
||||||
|
|
||||||
|
| データ種別 | 保存先 | 形式 | 理由 |
|
||||||
|
|-----------|----------|-------------|------|
|
||||||
|
| アプリケーションデータ | SQLite(`./data/app.db`) | リレーショナル | トランザクション・外部キー・インデックス・論理削除を一貫管理 |
|
||||||
|
| アップロードファイル | ローカルFS(`./data/uploads/<projectId>/`) | バイナリ | 外部ストレージ不要・自己完結。DBにメタデータ(`file_assets`)を保存 |
|
||||||
|
| Migration履歴 | SQLite(`schema_migrations`) | 行レコード | 適用済みファイルを一意管理・再実行回避 |
|
||||||
|
| バックアップ | ローカルFS(`./backups/`) | ZIP | DBファイル+uploadsディレクトリをZIP化 |
|
||||||
|
|
||||||
|
### SQLite接続設定
|
||||||
|
|
||||||
|
- `journal_mode = WAL`: 読み書きの並行性向上
|
||||||
|
- `foreign_keys = ON`: 外部キー制約・カスケード削除を有効化
|
||||||
|
- 接続はシングルトン(`getDb()`)で共有。dbPathは `process.env.SQLITE_PATH ?? "./data/app.db"`
|
||||||
|
|
||||||
|
### バックアップ戦略
|
||||||
|
|
||||||
|
- **作成タイミング**: 管理者がバックアップ画面から手動実行
|
||||||
|
- **作成内容**: SQLite DBファイル + uploadsディレクトリをZIP化
|
||||||
|
- **保存先**: `./backups/backup-<timestamp>.zip`
|
||||||
|
- **世代管理**: 一覧表示・ダウンロード可能(世代数は運用で定義)
|
||||||
|
- **リストア手順**: サーバ停止 → ZIP展開 → `./data/app.db` と `./data/uploads/` を差替 → サーバ再起動
|
||||||
|
|
||||||
|
## パフォーマンス要件
|
||||||
|
|
||||||
|
### 応答時間
|
||||||
|
|
||||||
|
| 操作 | 目標時間 | 測定環境 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| チャットSSE配信遅延 | 送信から全クライアント受信まで3秒以内(95パーセンタイル) | 小規模同時接続(数十) |
|
||||||
|
| 一覧画面表示 | 1000件データ時1秒以内(掲示板・チャット履歴・ファイル一覧・Markdownメモ) | ページネーション20件/頁 |
|
||||||
|
| バックアップ作成 | DB+uploads合計100MB相当で30秒以内 | ローカルFS |
|
||||||
|
| Migration実行 | 1ファイル1トランザクション・失敗時即座ロールバック | 初回スキーマ適用 |
|
||||||
|
|
||||||
|
### リソース使用量
|
||||||
|
|
||||||
|
| リソース | 上限 | 理由 |
|
||||||
|
|---------|------|------|
|
||||||
|
| メモリ | Next.jsサーバプロセス1GB程度 | 1プロジェクト数十人規模・小規模同時接続前提 |
|
||||||
|
| CPU | 単一サーバで十分 | SQLite同期処理・SSE数十接続を想定 |
|
||||||
|
| ディスク | DB+uploads合計数百MB〜数GBを想定 | バックアップは別途容量確保 |
|
||||||
|
|
||||||
|
### パフォーマンス最適化施策
|
||||||
|
|
||||||
|
- **ページネーション**: 全一覧APIで `?page=&pageSize=` を必須化し必要件数のみ取得
|
||||||
|
- **インデックス**: `project_notes(project_id)`, `project_notes(updated_at)` 等の高頻度検索カラムにインデックス付与
|
||||||
|
- **Server Components**: 可能な限りServer Componentsでデータ取得しクライアント送信量を削減
|
||||||
|
- **WALモード**: 読み書き並行性向上
|
||||||
|
- **SSE配信のスコープ限定**: プロジェクト単位でクライアント集合を管理し、無関係プロジェクトへの配信コストを排除
|
||||||
|
|
||||||
|
## セキュリティアーキテクチャ
|
||||||
|
|
||||||
|
### データ保護
|
||||||
|
|
||||||
|
- **パスワード保護**: bcryptでハッシュ化保存。平文保存禁止。ソルト付き・適切なコスト係数
|
||||||
|
- **暗号化**: 通信はHTTPS前提。保存時のDBファイル暗号化は本スコープ外(ローカルFSのアクセス権限で保護)
|
||||||
|
- **アクセス制御**: サーバプロセスのファイル権限により `./data/`, `./backups/` を保護
|
||||||
|
- **機密情報管理**: 設定は `.env` で管理(`SQLITE_PATH` 等)。コード内にハードコード禁止。`.env` はリポジトリにコミットしない
|
||||||
|
|
||||||
|
### 認証・認可
|
||||||
|
|
||||||
|
- **認証**: 独自ログイン方式・セッションベース。未認証リクエストは保護画面/APIにアクセス不可(401/リダイレクト)
|
||||||
|
- **認可(プロジェクト)**: Service層で `ProjectMemberRepository.isMember(projectId, userId)` を必ず実施。非参加者は403
|
||||||
|
- **認可(管理者機能)**: バックアップ・Migration状態確認は `role='system_admin'` のみ許可
|
||||||
|
- **ファイルアクセス**: ダウンロードAPIでもプロジェクト参加権限をチェック
|
||||||
|
|
||||||
|
### 入力バリデーション
|
||||||
|
|
||||||
|
- **バリデーション**: 全入力で必須チェック・長さ制限(タイトル1-200文字等)・形式チェックをService層で実施
|
||||||
|
- **SQL対策**: 全SQLをパラメータバインドで実行。文字列結合によるSQL構築は禁止
|
||||||
|
- **Markdownサニタイズ**: HTML直接入力無効化 + rehype-sanitize でサニタイズ + 危険URLスキーム(`javascript:`等)除外
|
||||||
|
- **ファイルアップロード**: MIMEタイプチェック・ファイル名サニタイズ・保存名の一意化(`<uuid>.<ext>`)
|
||||||
|
- **エラー表示**: スタックトレース・内部情報を本番では非表示。ユーザーには抽象メッセージのみ表示
|
||||||
|
|
||||||
|
### 監査
|
||||||
|
|
||||||
|
- 管理者操作・主要な変更操作をアクティビティログに記録(`activity_logs` テーブル)
|
||||||
|
|
||||||
|
## スケーラビリティ設計
|
||||||
|
|
||||||
|
### データ増大への対応
|
||||||
|
|
||||||
|
- **想定データ量**: 1プロジェクトあたり数十人・数千〜数万レコード(チャット・掲示板・アクティビティログが主要)
|
||||||
|
- **性能劣化対策**:
|
||||||
|
- ページネーションによる取得件数制限
|
||||||
|
- インデックス最適化(検索頻度高いカラム)
|
||||||
|
- 論理削除データの取得除外(`deleted_at IS NULL` を全取得クエリに付与)
|
||||||
|
- **アーカイブ戦略**: 完了/アーカイブ済みプロジェクトは `status='archived'` で残置。大量データの物理削除は本スコープ外(運用で判断)
|
||||||
|
|
||||||
|
### 拡張性
|
||||||
|
|
||||||
|
- **プラグインシステム**: なし(本スコープ外)
|
||||||
|
- **設定カスタマイズ**: `.env` による設定管理(`SQLITE_PATH`・ uploadsパス等)
|
||||||
|
- **API拡張性**: Route Handlerベースでリソースごとにエンドポイントを分割。将来の公開REST API化はPost-MVP
|
||||||
|
- **SSEイベント拡張**: `SseEvent` 型に新種別を追加するだけで新イベント配信が可能
|
||||||
|
|
||||||
|
## テスト戦略
|
||||||
|
|
||||||
|
### Unit Test
|
||||||
|
- **フレームワーク**: Vitest
|
||||||
|
- **対象**: SQLラッパー・Migration・全Repository・全Service・権限チェック・バリデーション・スケジュール重複判定・通知作成ロジック・アクティビティログ作成ロジック・マイルストーン進捗計算
|
||||||
|
- **カバレッジ目標**: Repository/Service層 80%以上
|
||||||
|
- **実行コマンド**: `npm test`
|
||||||
|
|
||||||
|
### 統合テスト
|
||||||
|
- **方法**: Vitestで実際のSQLite(一時ファイル)を使用
|
||||||
|
- **対象**: 認証フロー・プロジェクト作成→メンバー追加→権限分離・チャット送信→SSE配信
|
||||||
|
|
||||||
|
### E2E Test
|
||||||
|
- **ツール**: Playwright
|
||||||
|
- **シナリオ**: 認証・プロジェクト管理・掲示板・チャット(SSEリアルタイム)・ToDo(ドラッグ&ドロップ)・ファイル共有(Lightbox)・Markdownメモ・カレンダー・ミーティング(予定重複警告)・通知・アクティビティログ・バックアップ
|
||||||
|
- **実行コマンド**: `npm run test:e2e`
|
||||||
|
|
||||||
|
## 技術的制約
|
||||||
|
|
||||||
|
### 環境要件
|
||||||
|
- **OS**: devcontainer(開発)/ Linux想定(本番)。Windows・macOSでもNode.js Runtime動作可能
|
||||||
|
- **最小メモリ**: 1GB
|
||||||
|
- **必要ディスク**: DB+uploads+backups(数百MB〜数GB)
|
||||||
|
- **必須外部依存**: なし(外部DB・外部ストレージ・外部IdPに非依存・自己完結)
|
||||||
|
|
||||||
|
### パフォーマンス制約
|
||||||
|
- 1プロジェクトあたり数十人規模を想定(数百人規模はスコープ外)
|
||||||
|
- チャットSSEは小規模同時接続(数十接続)を前提
|
||||||
|
- 一覧取得は必ずページネーション(全件取得禁止)
|
||||||
|
|
||||||
|
### セキュリティ制約
|
||||||
|
- 認証必須(`/api/auth/*`除く)
|
||||||
|
- 全SQLパラメータバインド(文字列結合禁止)
|
||||||
|
- Markdown表示時のHTML無効化・サニタイズ必須
|
||||||
|
- パスワード平文保存禁止
|
||||||
|
|
||||||
|
### ランタイム制約
|
||||||
|
- Edge Runtime使用禁止(SQLite直接操作のため全てNode.js Runtime)
|
||||||
|
- Prisma使用禁止
|
||||||
|
|
||||||
|
## 依存管理
|
||||||
|
|
||||||
|
| ライブラリ | 目的 | バージョン管理方針 |
|
||||||
|
|-----------|------|-------------------|
|
||||||
|
| next | フレームワーク | 固定(破壊的変更リスク大) |
|
||||||
|
| react / react-dom | UI | ^(マイナーアップ許容) |
|
||||||
|
| better-sqlite3 | SQLiteドライバ | ^(ネイティブビルド要注意) |
|
||||||
|
| react-markdown / remark-gfm / rehype-sanitize | Markdown | ^(マイナーアップ許容) |
|
||||||
|
| bcrypt | パスワードハッシュ | ^ |
|
||||||
|
| tailwindcss | スタイリング | ^ |
|
||||||
|
| typescript | 型チェック | ~(パッチのみ自動・devDependencies) |
|
||||||
|
| vitest | Unit Test | ^(devDependencies) |
|
||||||
|
| @playwright/test | E2E Test | ^(devDependencies) |
|
||||||
|
| eslint | リンタ | ^(devDependencies) |
|
||||||
|
|
||||||
|
**方針**:
|
||||||
|
- 安定版は `^` でマイナーアップを許容
|
||||||
|
- 破壊的変更リスクのあるもの(next等)は固定
|
||||||
|
- devDependenciesは `~` でパッチのみ自動アップ許容
|
||||||
|
- package-lock.jsonで厳密にロックしCIで再現性を担保
|
||||||
|
|
||||||
|
## チェックリスト
|
||||||
|
|
||||||
|
- [x] 全技術選定に理由が記載されている
|
||||||
|
- [x] レイヤードアーキテクチャが明確に定義されている(依存規則含む)
|
||||||
|
- [x] パフォーマンス要件が測定可能である
|
||||||
|
- [x] セキュリティ考慮事項が文書化されている
|
||||||
|
- [x] スケーラビリティが考慮されている
|
||||||
|
- [x] バックアップ戦略が定義されている
|
||||||
|
- [x] 依存管理方針が明確である
|
||||||
|
- [x] テスト戦略が定義されている
|
||||||
761
docs/development-guidelines.md
Normal file
761
docs/development-guidelines.md
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
# 開発ガイドライン (Development Guidelines)
|
||||||
|
|
||||||
|
> 本書はシンプルグループウェアのチーム開発におけるコーディング規約と開発プロセスを定義する。技術スタックは `docs/architecture.md`、ディレクトリ構成は `docs/repository-structure.md` に基づく。
|
||||||
|
|
||||||
|
## コーディング規約
|
||||||
|
|
||||||
|
### 命名規約
|
||||||
|
|
||||||
|
#### 変数・関数
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: 役割が明確
|
||||||
|
const projectMembers = await projectMemberRepository.findByProject(projectId);
|
||||||
|
function formatDueDate(dueDate: string): string { }
|
||||||
|
const hasPermission = await projectMemberRepository.isMember(projectId, userId);
|
||||||
|
|
||||||
|
// ❌ 悪い例: 曖昧
|
||||||
|
const data = await repo.find(id);
|
||||||
|
function calc(arr: any[]): number { }
|
||||||
|
```
|
||||||
|
|
||||||
|
**原則**:
|
||||||
|
- 変数: camelCase・名詞または名詞句
|
||||||
|
- 関数: camelCase・動詞始まり(`find`, `create`, `update`, `delete`, `format`, `validate`)
|
||||||
|
- 定数: UPPER_SNAKE_CASE(`MAX_PAGE_SIZE`, `DEFAULT_PAGE_SIZE`)
|
||||||
|
- 真偽値: `is`, `has`, `should`, `can` 始まり(`isMember`, `hasPermission`)
|
||||||
|
|
||||||
|
#### クラス・インターフェース・型
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// クラス: PascalCase + 役割接尾
|
||||||
|
class UserRepository { }
|
||||||
|
class ChatService { }
|
||||||
|
class SqliteDatabase { }
|
||||||
|
|
||||||
|
// インターフェース: PascalCase(接尾辞Iは付けない)
|
||||||
|
interface ProjectMember { }
|
||||||
|
interface CreateMeetingInput { }
|
||||||
|
|
||||||
|
// 型エイリアス: PascalCase
|
||||||
|
type ProjectStatus = 'active' | 'on_hold' | 'completed' | 'archived';
|
||||||
|
type NotificationType = 'mention' | 'todo_assigned' | ...;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ファイル名
|
||||||
|
|
||||||
|
- Repository/Serviceクラス: PascalCase + 接尾(`UserRepository.ts`, `ChatService.ts`)
|
||||||
|
- 関数・ユーティリティ: camelCase・動詞始まり(`formatDate.ts`, `validateEmail.ts`)
|
||||||
|
- 型定義: PascalCase(`TodoItem.ts`)
|
||||||
|
- Reactコンポーネント: PascalCase(`KanbanBoard.tsx`)
|
||||||
|
- Route Handler/画面: Next.js固定名(`route.ts`, `page.tsx`, `layout.tsx`)
|
||||||
|
- Migration: `NNN_description.sql`(`001_initial.sql`)
|
||||||
|
|
||||||
|
### コードフォーマット
|
||||||
|
|
||||||
|
- **インデント**: 2スペース
|
||||||
|
- **行長**: 最大100文字
|
||||||
|
- **セミコロン**: 必須
|
||||||
|
- **クォート**: シングルクォート
|
||||||
|
- **ツール**: Prettier(`.prettierrc`)+ ESLint(`eslint.config.js` Flat Config)で自動整形
|
||||||
|
|
||||||
|
### TypeScript規約
|
||||||
|
|
||||||
|
#### 型定義
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: 明示的な型注釈
|
||||||
|
function findByProject(projectId: number): ProjectMember[] { }
|
||||||
|
|
||||||
|
// ❌ 悪い例: 型推論への過度な依存(暗黙のany)
|
||||||
|
function findByProject(projectId) { }
|
||||||
|
```
|
||||||
|
|
||||||
|
- 公開APIの引数・戻り値には明示的な型注釈を付ける
|
||||||
|
- `any` は原則禁止。やむを得ない場合は `unknown` + 型ガードを使用
|
||||||
|
- オブジェクト型は `interface`、共用型・プリミティブ型は `type` エイリアスを使用
|
||||||
|
- Entity型は `lib/types/` に集約し、レイヤ間で共有する
|
||||||
|
|
||||||
|
#### 関数設計
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: 単一責任・パラメータをオブジェクトに集約
|
||||||
|
interface CreateTodoInput {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
assigneeId?: number;
|
||||||
|
dueDate?: string;
|
||||||
|
priority?: TodoPriority;
|
||||||
|
}
|
||||||
|
function createTodo(input: CreateTodoInput): TodoItem { }
|
||||||
|
|
||||||
|
// ❌ 悪い例: 多すぎるパラメータ
|
||||||
|
function createTodo(title, description, assigneeId, dueDate, priority, milestoneId): TodoItem { }
|
||||||
|
```
|
||||||
|
|
||||||
|
- 1関数の責務は単一に(目安20行以内・50行推奨上限)
|
||||||
|
- パラメータが4つ超の場合はオブジェクトに集約
|
||||||
|
- 1ファイル300行以下推奨・500行超は分割
|
||||||
|
|
||||||
|
### コメント規約
|
||||||
|
|
||||||
|
#### ドキュメントコメント(TSDoc)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* プロジェクトにメンバーを追加する
|
||||||
|
*
|
||||||
|
* @param actorId - 操作実行者のユーザーID(権限チェックに使用)
|
||||||
|
* @param projectId - 対象プロジェクトID
|
||||||
|
* @param userId - 追加するユーザーID
|
||||||
|
* @param role - プロジェクト内ロール
|
||||||
|
* @throws {ForbiddenError} 実行者に権限がない場合
|
||||||
|
* @throws {NotFoundError} プロジェクトまたはユーザーが存在しない場合
|
||||||
|
*/
|
||||||
|
async function addMember(
|
||||||
|
actorId: number,
|
||||||
|
projectId: number,
|
||||||
|
userId: number,
|
||||||
|
role: ProjectMemberRole
|
||||||
|
): Promise<void> { }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### インラインコメント
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: 理由を説明する
|
||||||
|
// 論理削除済みデータを除外するため deleted_at IS NULL を付与
|
||||||
|
const threads = db.query<BoardThread>(`
|
||||||
|
SELECT * FROM board_threads
|
||||||
|
WHERE project_id = @projectId AND deleted_at IS NULL
|
||||||
|
`, { projectId });
|
||||||
|
|
||||||
|
// ❌ 悪い例: コードの再述
|
||||||
|
// スレッドを取得する
|
||||||
|
const threads = db.query(...);
|
||||||
|
```
|
||||||
|
|
||||||
|
- コードから自明な内容は書かない
|
||||||
|
- 「なぜ」その処理をするかを書く
|
||||||
|
- TODO/FIXMEは課題番号と共に記載(`// TODO: キャッシュを実装する (Issue #123)`)
|
||||||
|
- コメントアウトされたコードは残さない(削除する)
|
||||||
|
|
||||||
|
### エラーハンドリング
|
||||||
|
|
||||||
|
#### カスタムエラークラス
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 期待されるエラー: 適切なエラークラスを定義
|
||||||
|
class ValidationError extends Error {
|
||||||
|
constructor(message: string, public field: string, public value: unknown) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ValidationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ForbiddenError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ForbiddenError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotFoundError extends Error {
|
||||||
|
constructor(public resource: string, public id: number | string) {
|
||||||
|
super(`${resource} not found: ${id}`);
|
||||||
|
this.name = 'NotFoundError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### エラー処理パターン
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: 期待されるエラーは適切に処理、予期せぬエラーは伝播
|
||||||
|
async function getThread(threadId: number): Promise<BoardThread> {
|
||||||
|
const thread = await boardRepository.findThreadById(threadId);
|
||||||
|
if (!thread) {
|
||||||
|
throw new NotFoundError('BoardThread', threadId);
|
||||||
|
}
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route HandlerでのHTTPステータスへの変換
|
||||||
|
try {
|
||||||
|
const thread = await boardService.getThread(threadId);
|
||||||
|
return Response.json(thread);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError) return Response.json({ error: { message: error.message } }, { status: 400 });
|
||||||
|
if (error instanceof ForbiddenError) return Response.json({ error: { message: error.message } }, { status: 403 });
|
||||||
|
if (error instanceof NotFoundError) return Response.json({ error: { message: error.message } }, { status: 404 });
|
||||||
|
console.error('Unexpected error:', error);
|
||||||
|
return Response.json({ error: { message: '内部エラーが発生しました' } }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ 悪い例: エラーを無視して null を返す
|
||||||
|
async function getThread(threadId: number): Promise<BoardThread | null> {
|
||||||
|
try {
|
||||||
|
return await boardRepository.findThreadById(threadId);
|
||||||
|
} catch (error) {
|
||||||
|
return null; // エラー情報が失われる
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**原則**:
|
||||||
|
- 期待されるエラー(バリデーション・権限・存在確認)は適切なエラークラスで表現
|
||||||
|
- 予期せぬエラーは上位に伝播しログに記録
|
||||||
|
- エラーを無視(空catch)しない
|
||||||
|
- エラーメッセージは具体的で解決策を示す(`'タイトルは1-200文字で入力してください。現在: 250文字'`)
|
||||||
|
|
||||||
|
## プロジェクト固有規約
|
||||||
|
|
||||||
|
### Repository層の規約
|
||||||
|
|
||||||
|
#### SQLは必ずパラメータバインド
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: パラメータバインド
|
||||||
|
const user = db.get<User>('SELECT * FROM users WHERE email = @email', { email });
|
||||||
|
|
||||||
|
// ❌ 悪い例: 文字列結合(SQLインジェクション脆弱性)
|
||||||
|
const user = db.get<User>(`SELECT * FROM users WHERE email = '${email}'`);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 論理削除テーブルの取得には必ず deleted_at IS NULL
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例
|
||||||
|
const notes = db.query<ProjectNote>(`
|
||||||
|
SELECT * FROM project_notes
|
||||||
|
WHERE project_id = @projectId AND deleted_at IS NULL
|
||||||
|
ORDER BY is_pinned DESC, updated_at DESC
|
||||||
|
LIMIT @limit OFFSET @offset
|
||||||
|
`, { projectId, limit, offset });
|
||||||
|
|
||||||
|
// ❌ 悪い例: 削除済みデータが混入
|
||||||
|
const notes = db.query<ProjectNote>(`SELECT * FROM project_notes WHERE project_id = @projectId`, { projectId });
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Repositoryは直接SQLiteライブラリを触らない
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: SQLラッパー経由
|
||||||
|
import { getDb } from '@/lib/db/sqlite';
|
||||||
|
const db = getDb();
|
||||||
|
db.execute('INSERT INTO projects ...', params);
|
||||||
|
|
||||||
|
// ❌ 悪い例: better-sqlite3を直接操作
|
||||||
|
import Database from 'better-sqlite3';
|
||||||
|
const db = new Database('./data/app.db');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ページネーション
|
||||||
|
|
||||||
|
一覧取得APIは必ずページネーション(`LIMIT`/`OFFSET`)し、全件取得しない。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function findThreads(projectId: number, page: number, pageSize: number = 20) {
|
||||||
|
const offset = (page - 1) * pageSize;
|
||||||
|
return db.query<BoardThread>(`
|
||||||
|
SELECT * FROM board_threads
|
||||||
|
WHERE project_id = @projectId AND deleted_at IS NULL
|
||||||
|
ORDER BY is_pinned DESC, created_at DESC
|
||||||
|
LIMIT @pageSize OFFSET @offset
|
||||||
|
`, { projectId, pageSize, offset });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service層の規約
|
||||||
|
|
||||||
|
#### 権限チェックを必ず実施
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 良い例: 操作前に権限チェック
|
||||||
|
async function addMember(actorId: number, projectId: number, userId: number, role: ProjectMemberRole) {
|
||||||
|
const actorRole = await projectMemberRepository.getRole(projectId, actorId);
|
||||||
|
if (!actorRole || actorRole !== 'admin') {
|
||||||
|
throw new ForbiddenError('プロジェクト管理者のみメンバー追加が可能です');
|
||||||
|
}
|
||||||
|
// ...メンバー追加処理
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### トランザクション境界の明示
|
||||||
|
|
||||||
|
複数テーブルを更新する場合はトランザクション内で実行する。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function createMeeting(actorId: number, projectId: number, input: MeetingInput) {
|
||||||
|
return db.transaction(() => {
|
||||||
|
const meeting = meetingRepository.create({ ...input, projectId, createdById: actorId });
|
||||||
|
meetingRepository.addMembers(meeting.id, input.memberIds);
|
||||||
|
activityLogService.log({ projectId, actorId, action: 'meeting_created', targetType: 'meeting', targetId: meeting.id });
|
||||||
|
return meeting;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 副作用の分離
|
||||||
|
|
||||||
|
通知生成・アクティビティログ記録・SSE配信は専用Service(`NotificationService`, `ActivityLogService`, `SseHub`)に委譲し、業務ロジックと分離する。
|
||||||
|
|
||||||
|
### Next.js規約
|
||||||
|
|
||||||
|
#### Node.js Runtimeの明示
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// app/api/.../route.ts の先頭
|
||||||
|
export const runtime = 'nodejs'; // Edge Runtime使用禁止
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Server Componentsを優先
|
||||||
|
|
||||||
|
データ取得は可能な限りServer Componentsで行い、クライアント送信量を削減する。インタラクティブな要素(ドラッグ&ドロップ・SSE受信・フォーム)のみClient Componentsとする。
|
||||||
|
|
||||||
|
#### Markdownレンダリングの安全性
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 必ず rehype-sanitize を通す
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import rehypeSanitize from 'rehype-sanitize';
|
||||||
|
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeSanitize]}>
|
||||||
|
{bodyMd}
|
||||||
|
</ReactMarkdown>
|
||||||
|
```
|
||||||
|
|
||||||
|
HTML直接入力は無効化し、危険なURLスキーム(`javascript:`等)は除外する。
|
||||||
|
|
||||||
|
### セキュリティ規約
|
||||||
|
|
||||||
|
- 機密情報(パスワード・APIキー)はコードにハードコードしない。`.env` で管理
|
||||||
|
- パスワードは bcrypt でハッシュ化保存
|
||||||
|
- ファイルアップロードはMIMEタイプチェック・ファイル名サニタイズ・保存名の一意化
|
||||||
|
- ファイルアクセスAPIでもプロジェクト参加権限をチェック
|
||||||
|
- 管理者機能(バックアップ・Migration状態)は `role='system_admin'` のみ許可
|
||||||
|
|
||||||
|
## Gitワークフロー規則
|
||||||
|
|
||||||
|
### ブランチ戦略(Git Flow)
|
||||||
|
|
||||||
|
```
|
||||||
|
main (本番環境)
|
||||||
|
└── develop (開発統合)
|
||||||
|
├── feature/task-management
|
||||||
|
├── feature/user-auth
|
||||||
|
├── fix/chat-sse-reconnect
|
||||||
|
└── refactor/todo-repository
|
||||||
|
```
|
||||||
|
|
||||||
|
**運用ルール**:
|
||||||
|
- `main`: リリース済みの安定コードのみ。タグでバージョン管理
|
||||||
|
- `develop`: 次回リリースの最新開発コード。CIで自動テスト実行
|
||||||
|
- `feature/*`, `fix/*`: developから分岐しPR経由でdevelopへマージ
|
||||||
|
- `release/*`: リリース準備(必要に応じて)
|
||||||
|
- `hotfix/*`: 本番障害対応(mainから分岐しmain/develop両方へマージ)
|
||||||
|
- 直接コミット禁止: 全ブランチでPRレビュー必須
|
||||||
|
- マージ方針: feature→develop はsquash merge推奨、develop→main はmerge commit
|
||||||
|
|
||||||
|
### コミットメッセージ規約(Conventional Commits)
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
**type**:
|
||||||
|
- `feat`: 新機能
|
||||||
|
- `fix`: バグ修正
|
||||||
|
- `docs`: ドキュメント
|
||||||
|
- `style`: フォーマット(コード挙動への影響なし)
|
||||||
|
- `refactor`: リファクタリング
|
||||||
|
- `perf`: パフォーマンス改善
|
||||||
|
- `test`: テスト追加・修正
|
||||||
|
- `build`: ビルドシステム
|
||||||
|
- `ci`: CI/CD設定
|
||||||
|
- `chore`: その他(依存更新等)
|
||||||
|
|
||||||
|
**例**:
|
||||||
|
```
|
||||||
|
feat(chat): SSEによるリアルタイムメッセージ配信を実装
|
||||||
|
|
||||||
|
プロジェクト別のSSEエンドポイントを追加し、メッセージ送信時に
|
||||||
|
参加メンバーへリアルタイム配信する。
|
||||||
|
|
||||||
|
- SseHubクラスをlib/sse/hub.tsに追加
|
||||||
|
- ChatService.sendMessageでブロードキャスト
|
||||||
|
- chat.message.created/updated/deletedイベント定義
|
||||||
|
|
||||||
|
Closes #42
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pull Requestプロセス
|
||||||
|
|
||||||
|
**PR作成前チェック**:
|
||||||
|
- [ ] 全テスト成功(`npm test`, `npm run test:e2e`)
|
||||||
|
- [ ] Lintエラーなし(`npm run lint`)
|
||||||
|
- [ ] 型チェック成功(`npm run typecheck`)
|
||||||
|
- [ ] コンフリクト解消済み
|
||||||
|
|
||||||
|
**PRテンプレート**:
|
||||||
|
```markdown
|
||||||
|
## 変更種別
|
||||||
|
- [ ] 新機能 (feat)
|
||||||
|
- [ ] バグ修正 (fix)
|
||||||
|
- [ ] リファクタリング (refactor)
|
||||||
|
- [ ] ドキュメント (docs)
|
||||||
|
- [ ] その他 (chore)
|
||||||
|
|
||||||
|
## 概要
|
||||||
|
[変更内容の簡潔な説明]
|
||||||
|
|
||||||
|
## 変更理由
|
||||||
|
[なぜこの変更が必要か]
|
||||||
|
|
||||||
|
## 変更内容
|
||||||
|
- [変更1]
|
||||||
|
- [変更2]
|
||||||
|
|
||||||
|
## テスト
|
||||||
|
- [ ] Unit Test追加
|
||||||
|
- [ ] E2E Test追加
|
||||||
|
- [ ] 手動テスト実施
|
||||||
|
|
||||||
|
テスト結果: [説明]
|
||||||
|
|
||||||
|
## 関連Issue
|
||||||
|
Closes #[番号]
|
||||||
|
|
||||||
|
## レビューポイント
|
||||||
|
[特に確認してほしい点]
|
||||||
|
```
|
||||||
|
|
||||||
|
**レビュープロセス**:
|
||||||
|
1. セルフレビュー
|
||||||
|
2. 自動テスト実行(CI)
|
||||||
|
3. レビュアー割当
|
||||||
|
4. レビュー指摘対応
|
||||||
|
5. 承認後にマージ
|
||||||
|
|
||||||
|
**PRサイズ目安**:
|
||||||
|
- 小PR(100行以下): 推奨
|
||||||
|
- 中PR(100-300行): 許容
|
||||||
|
- 大PR(300行超): 分割を検討
|
||||||
|
|
||||||
|
## テスト戦略
|
||||||
|
|
||||||
|
### テスト実装の必須条件
|
||||||
|
|
||||||
|
本プロジェクトでは、品質担保のため **Unit Test(Vitest)と E2E Test(Playwright)の実装を必須とする。** テストが未実装・未成功のPRはマージ不可。
|
||||||
|
|
||||||
|
#### Unit Test(Vitest)の実装【必須】
|
||||||
|
|
||||||
|
以下の対象について Unit Test の実装を必須とする。新規実装・修正時に対応する Unit Test を必ず作成すること。
|
||||||
|
|
||||||
|
- SQLラッパー
|
||||||
|
- Migration実行
|
||||||
|
- 全Repositoryクラス(User / Project / ProjectMember / Board / Chat / Todo / File / Calendar / Meeting / ProjectNote / Notification / ActivityLog)
|
||||||
|
- 全Serviceクラス(Auth / Project / Chat / Meeting / Schedule / FileStorage / Backup)
|
||||||
|
- 権限チェック・バリデーション・スケジュール重複判定・通知作成ロジック・アクティビティログ作成ロジック・マイルストーン進捗計算
|
||||||
|
|
||||||
|
**合格基準**:
|
||||||
|
- [ ] `npm test` で全件成功すること
|
||||||
|
- [ ] Repository/Service層のカバレッジ 80%以上を維持すること
|
||||||
|
- [ ] 正常系・異常系(権限エラー・バリデーションエラー・存在確認)を網羅すること
|
||||||
|
|
||||||
|
#### E2E Test(Playwright)の実装【必須】
|
||||||
|
|
||||||
|
主要ユーザーフローについて Playwright による E2E Test の実装を必須とする。機能追加時は該当フローの E2E Test を必ず作成すること。
|
||||||
|
|
||||||
|
対象フロー:
|
||||||
|
- 認証(ログイン・ログアウト・未ログインの保護)
|
||||||
|
- プロジェクト管理(作成・編集・メンバー追加/削除・アーカイブ)
|
||||||
|
- 掲示板(スレッド作成・編集・コメント・検索)
|
||||||
|
- チャット(送信・SSEリアルタイム受信・編集・削除)
|
||||||
|
- ToDo / Kanban(カラム作成・タスク作成・編集・移動・担当者/期限設定・完了)
|
||||||
|
- ファイル共有(アップロード・一覧・Lightbox閲覧・PDFプレビュー・削除)
|
||||||
|
- Markdownメモ(作成・編集・プレビュー・ピン留め・検索・削除)
|
||||||
|
- カレンダー(ToDo期限・マイルストーン・ミーティング表示・イベント作成/編集)
|
||||||
|
- ミーティング(作成・参加メンバー設定・予定重複警告・アジェンダ/議事録・関連付け)
|
||||||
|
- 通知(ToDo担当者・メンション・ミーティング参加者への通知・既読化)
|
||||||
|
- アクティビティログ(各操作の記録)
|
||||||
|
- バックアップ(作成・一覧表示・ダウンロード)
|
||||||
|
|
||||||
|
**合格基準**:
|
||||||
|
- [ ] `npm run test:e2e` で全件成功すること
|
||||||
|
- [ ] 主要フローのカバレッジ 100%を維持すること
|
||||||
|
|
||||||
|
### テストピラミッド
|
||||||
|
|
||||||
|
```
|
||||||
|
/\
|
||||||
|
/E2E\ 少数(遅い・高コスト)
|
||||||
|
/------\
|
||||||
|
/ Integ. \ 中程度
|
||||||
|
/----------\
|
||||||
|
/ Unit \ 多数(高速・低コスト)
|
||||||
|
/--------------\
|
||||||
|
```
|
||||||
|
|
||||||
|
**対象比率**:
|
||||||
|
- Unit Test: 70%
|
||||||
|
- Integration Test: 20%
|
||||||
|
- E2E Test: 10%
|
||||||
|
|
||||||
|
### Unit Test(Vitest)
|
||||||
|
|
||||||
|
**対象**: SQLラッパー・Migration・全Repository・全Service・権限チェック・バリデーション・スケジュール重複判定・通知作成ロジック・アクティビティログ作成ロジック・マイルストーン進捗計算
|
||||||
|
|
||||||
|
**カバレッジ目標**: Repository/Service層 80%以上
|
||||||
|
|
||||||
|
**構造(Given-When-Then)**:
|
||||||
|
```typescript
|
||||||
|
describe('ChatService', () => {
|
||||||
|
describe('sendMessage', () => {
|
||||||
|
it('有効なデータでメッセージを作成できる', async () => {
|
||||||
|
// Given: セットアップ
|
||||||
|
const chatService = new ChatService(mockChatRepo, mockSseHub, mockNotificationService);
|
||||||
|
const input = { body: 'テストメッセージ' };
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = await chatService.sendMessage(actorId, projectId, input.body);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result.id).toBeDefined();
|
||||||
|
expect(result.body).toBe('テストメッセージ');
|
||||||
|
expect(mockSseHub.broadcast).toHaveBeenCalledWith(projectId, expect.objectContaining({ type: 'chat.message.created' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('プロジェクト非参加者はメッセージ送信時にForbiddenErrorを投げる', async () => {
|
||||||
|
// Given
|
||||||
|
const chatService = new ChatService(...);
|
||||||
|
mockProjectMemberRepository.isMember.mockReturnValue(false);
|
||||||
|
|
||||||
|
// When/Then
|
||||||
|
await expect(chatService.sendMessage(nonMemberId, projectId, 'body')).rejects.toThrow(ForbiddenError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**テスト命名**: `[対象]_[条件]_[期待結果]`
|
||||||
|
```typescript
|
||||||
|
it('findById_existingId_returnsThread', () => { });
|
||||||
|
it('findById_nonExistentId_returnsNull', () => { });
|
||||||
|
it('create_emptyTitle_throwsValidationError', () => { });
|
||||||
|
```
|
||||||
|
|
||||||
|
**モック原則**: 外部依存(DB・ファイルシステム・SSE)はモック化、業務ロジックは実体を使用。
|
||||||
|
|
||||||
|
### Integration Test(Vitest)
|
||||||
|
|
||||||
|
**対象**: 複数コンポーネントの連携。実際のSQLite(一時ファイル)を使用。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
describe('プロジェクトメンバー権限', () => {
|
||||||
|
it('非参加者はプロジェクトデータにアクセスできない', async () => {
|
||||||
|
// 実DBでプロジェクト・ユーザー作成
|
||||||
|
const project = await projectService.createProject(ownerId, { name: 'P1' });
|
||||||
|
await expect(boardService.listThreads(nonMemberId, project.id)).rejects.toThrow(ForbiddenError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### E2E Test(Playwright)
|
||||||
|
|
||||||
|
**対象**: 主要ユーザーフローの全体検証。`tests/e2e/*.spec.ts`。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test('チャットメッセージがSSEでリアルタイム配信される', async ({ browser }) => {
|
||||||
|
const contextA = await browser.newContext();
|
||||||
|
const contextB = await browser.newContext();
|
||||||
|
const pageA = await contextA.newPage();
|
||||||
|
const pageB = await contextB.newPage();
|
||||||
|
|
||||||
|
// 両ブラウザで同プロジェクトのチャット画面を開く
|
||||||
|
await pageA.goto('/projects/1/chat');
|
||||||
|
await pageB.goto('/projects/1/chat');
|
||||||
|
|
||||||
|
// Aがメッセージ送信
|
||||||
|
await pageA.fill('input[name=message]', 'こんにちは');
|
||||||
|
await pageA.click('button[type=submit]');
|
||||||
|
|
||||||
|
// Bにリアルタイム表示される
|
||||||
|
await expect(pageB.locator('text=こんにちは')).toBeVisible();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**カバレッジ目標**: 主要フロー100%(認証・プロジェクト管理・掲示板・チャット・ToDo・ファイル・メモ・カレンダー・ミーティング・通知・アクティビティ・バックアップ)
|
||||||
|
|
||||||
|
### 実行コマンド
|
||||||
|
|
||||||
|
| コマンド | 説明 |
|
||||||
|
|---------|------|
|
||||||
|
| `npm test` | Unit Test実行(Vitest) |
|
||||||
|
| `npm run test:e2e` | E2E Test実行(Playwright) |
|
||||||
|
| `npm run lint` | Lint実行(ESLint) |
|
||||||
|
| `npm run typecheck` | 型チェック実行(tsc --noEmit) |
|
||||||
|
| `npm run build` | ビルド |
|
||||||
|
|
||||||
|
## コードレビュー基準
|
||||||
|
|
||||||
|
### レビューポイント
|
||||||
|
|
||||||
|
**機能性**:
|
||||||
|
- [ ] 要件を満たしているか
|
||||||
|
- [ ] エッジケースが考慮されているか
|
||||||
|
- [ ] エラーハンドリングが適切か
|
||||||
|
|
||||||
|
**可読性**:
|
||||||
|
- [ ] 命名が明確か
|
||||||
|
- [ ] コメントが適切か
|
||||||
|
- [ ] 複雑なロジックに説明があるか
|
||||||
|
|
||||||
|
**保守性**:
|
||||||
|
- [ ] 重複コードがないか
|
||||||
|
- [ ] 責務が分離されているか
|
||||||
|
- [ ] 変更の影響範囲が限定的か
|
||||||
|
|
||||||
|
**パフォーマンス**:
|
||||||
|
- [ ] 不要な計算がないか
|
||||||
|
- [ ] N+1クエリになっていないか
|
||||||
|
- [ ] 一覧取得がページネーションされているか
|
||||||
|
|
||||||
|
**セキュリティ**:
|
||||||
|
- [ ] 入力バリデーションが適切か
|
||||||
|
- [ ] SQLがパラメータバインドされているか
|
||||||
|
- [ ] 権限チェックが実装されているか
|
||||||
|
- [ ] 機密情報のハードコードがないか
|
||||||
|
- [ ] Markdownがサニタイズされているか
|
||||||
|
|
||||||
|
### レビューコメントの書き方
|
||||||
|
|
||||||
|
**建設的フィードバック**:
|
||||||
|
```markdown
|
||||||
|
// ✅ 良い例
|
||||||
|
この実装だとメンバー数増加時にN+1クエリになります。
|
||||||
|
JOINで一括取得するのはいかがでしょうか?
|
||||||
|
|
||||||
|
// ❌ 悪い例
|
||||||
|
この書き方は良くないです。
|
||||||
|
```
|
||||||
|
|
||||||
|
**優先度の明示**:
|
||||||
|
- `[必須]`: 修正必須(セキュリティ・バグ等)
|
||||||
|
- `[推奨]`: 修正推奨
|
||||||
|
- `[提案]`: 検討提案
|
||||||
|
- `[質問]`: 意図の確認
|
||||||
|
|
||||||
|
## 開発環境セットアップ
|
||||||
|
|
||||||
|
### 必須ツール
|
||||||
|
|
||||||
|
| ツール | バージョン | インストール方法 |
|
||||||
|
|--------|-----------|-----------------|
|
||||||
|
| Node.js | v24.11.0 | 公式インストーラ/nvm |
|
||||||
|
| npm | 11.x | Node.jsにバンドル |
|
||||||
|
| devcontainer | - | VS Code拡張(開発環境統一) |
|
||||||
|
|
||||||
|
### セットアップ手順
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. リポジトリクローン
|
||||||
|
git clone [URL]
|
||||||
|
cd [project-name]
|
||||||
|
|
||||||
|
# 2. 依存関係インストール
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 3. 環境変数設定
|
||||||
|
cp .env.example .env
|
||||||
|
# .env を編集(SQLITE_PATH等)
|
||||||
|
|
||||||
|
# 4. DB初期化(Migration実行)
|
||||||
|
npm run migrate
|
||||||
|
|
||||||
|
# 5. 開発サーバ起動
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 品質自動化
|
||||||
|
|
||||||
|
**Pre-commit(Husky + lint-staged)**: コミット前にLint・フォーマット・型チェックを自動実行
|
||||||
|
|
||||||
|
```json
|
||||||
|
// package.json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"migrate": "tsx lib/db/run-migrations.ts",
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx}": ["eslint --fix", "prettier --write"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**CI(GitHub Actions)**: PR作成時にLint・型チェック・Unit Test・ビルドを自動実行
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/ci.yml
|
||||||
|
name: CI
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with: { node-version: '24' }
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run typecheck
|
||||||
|
- run: npm run test
|
||||||
|
- run: npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
**効果**: 欠陥コードの混入防止・早期発見による修正コスト削減・CIによる品質担保。
|
||||||
|
|
||||||
|
## チェックリスト
|
||||||
|
|
||||||
|
### 実装完了前
|
||||||
|
- [ ] 命名が明確で一貫している
|
||||||
|
- [ ] 関数が単一責務
|
||||||
|
- [ ] マジックナンバーがない
|
||||||
|
- [ ] 型注釈が適切
|
||||||
|
- [ ] エラーハンドリングが実装されている
|
||||||
|
|
||||||
|
### セキュリティ
|
||||||
|
- [ ] 入力バリデーション実装
|
||||||
|
- [ ] 機密情報のハードコードなし
|
||||||
|
- [ ] SQLパラメータバインド
|
||||||
|
- [ ] 権限チェック実装
|
||||||
|
- [ ] Markdownサニタイズ
|
||||||
|
|
||||||
|
### パフォーマンス
|
||||||
|
- [ ] 適切なデータ構造
|
||||||
|
- [ ] N+1クエリ回避
|
||||||
|
- [ ] 一覧のページネーション
|
||||||
|
|
||||||
|
### テスト【必須】
|
||||||
|
- [ ] Unit Test(Vitest)作成
|
||||||
|
- [ ] E2E Test(Playwright)作成
|
||||||
|
- [ ] `npm test` 全件成功
|
||||||
|
- [ ] `npm run test:e2e` 全件成功
|
||||||
|
- [ ] エッジケース網羅
|
||||||
|
|
||||||
|
### ツール
|
||||||
|
- [ ] Lintエラーなし
|
||||||
|
- [ ] 型チェック成功
|
||||||
|
- [ ] フォーマット統一
|
||||||
1077
docs/functional-design.md
Normal file
1077
docs/functional-design.md
Normal file
File diff suppressed because it is too large
Load Diff
1606
docs/ideas/initial-requirements.md
Normal file
1606
docs/ideas/initial-requirements.md
Normal file
File diff suppressed because it is too large
Load Diff
870
docs/milestones.md
Normal file
870
docs/milestones.md
Normal file
@ -0,0 +1,870 @@
|
|||||||
|
# マイルストーン・タスク定義書 (Milestones & Tasks)
|
||||||
|
|
||||||
|
> 本書は `docs/product-requirements.md`・`docs/functional-design.md`・`docs/architecture.md`・`docs/repository-structure.md`・`docs/development-guidelines.md` に基づき、プロジェクト全体のマイルストーンと各マイルストーンのタスクを詳細に定義する。機能漏れを防ぐため、末尾に「機能カバレッジマトリクス」を設ける。
|
||||||
|
|
||||||
|
## マイルストーン全体構成
|
||||||
|
|
||||||
|
PRDの開発フェーズ(Phase 1〜5)を依存関係を考慮して15マイルストーンに展開する。
|
||||||
|
|
||||||
|
| MS | 名称 | 対応フェーズ | 主な成果物 | 前提 |
|
||||||
|
|----|------|------------|-----------|------|
|
||||||
|
| M1 | プロジェクト基盤セットアップ | Phase 1 | Next.js環境・設定ファイル・CI | - |
|
||||||
|
| M2 | DB基盤(SQLラッパー・Migration) | Phase 1 | sqlite.ts・migrator.ts・初期スキーマ | M1 |
|
||||||
|
| M3 | 認証・ユーザー管理 | Phase 1 | AuthService・UserRepository・ログイン画面 | M2 |
|
||||||
|
| M4 | プロジェクト管理・メンバー管理 | Phase 1 | ProjectService・プロジェクト画面・レイアウト | M3 |
|
||||||
|
| M5 | 通知・アクティビティログ基盤 | Phase 2 | NotificationService・ActivityLogService | M4 |
|
||||||
|
| M6 | 掲示板 | Phase 2 | BoardService・掲示板画面 | M5 |
|
||||||
|
| M7 | Markdownメモ | Phase 2 | NoteService・メモエディタ・Markdownプレビュー | M5 |
|
||||||
|
| M8 | SSE基盤・チャット | Phase 3 | SseHub・ChatService・チャット画面 | M5 |
|
||||||
|
| M9 | ToDo / Kanban | Phase 3 | TodoService・Kanbanボード | M8 |
|
||||||
|
| M10 | ファイル共有・Lightbox | Phase 4 | FileStorageService・Lightbox | M4 |
|
||||||
|
| M11 | カレンダー・マイルストーン | Phase 4 | ScheduleService・カレンダーUI・進捗計算 | M9 |
|
||||||
|
| M12 | ミーティング管理・スケジュール重複 | Phase 5 | MeetingService・重複判定アルゴリズム | M11 |
|
||||||
|
| M13 | 検索・ダッシュボード完成 | (横断) | 横断検索・個人/プロジェクトダッシュボード | M6-M12 |
|
||||||
|
| M14 | バックアップ・管理者機能 | Phase 5 | BackupService・管理者画面 | M2 |
|
||||||
|
| M15 | 全体テスト完成・品質担保 | (全体) | 全Unit/E2E/統合テスト成功 | M1-M14 |
|
||||||
|
|
||||||
|
### マイルストーン進行ルール
|
||||||
|
|
||||||
|
- 各マイルストーン完了時に `npm run lint`・`npm run typecheck` が成功すること
|
||||||
|
- 各マイルストーンで実装したRepository/Service/画面には **Unit Test または E2E Test を必須で実装**する(`docs/development-guidelines.md` テスト実装の必須条件に準拠)
|
||||||
|
- テスト未実装・未成功のマイルストーンは完了扱いとしない
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M1: プロジェクト基盤セットアップ
|
||||||
|
|
||||||
|
**目的**: Next.js 15 + TypeScript + Tailwind CSS の開発環境を構築し、全レイヤのディレクトリ構成と品質自動化ツールを整える。
|
||||||
|
|
||||||
|
**前提**: なし
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**基盤・設定**:
|
||||||
|
- [ ] Next.js 15 プロジェクト初期化(App Router・TypeScript)
|
||||||
|
- [ ] Tailwind CSS セットアップ(`app/globals.css`・`tailwind.config`)
|
||||||
|
- [ ] `tsconfig.json` 設定(`@/*` パスエイリアス・strict モード)
|
||||||
|
- [ ] `next.config.mjs` 設定
|
||||||
|
- [ ] `.env.example` 作成(`SQLITE_PATH`・uploadsパス等)
|
||||||
|
- [ ] ディレクトリ構成作成: `app/`・`lib/`・`repositories/`・`services/`・`components/`・`tests/`・`data/`・`backups/`
|
||||||
|
|
||||||
|
**品質自動化ツール**:
|
||||||
|
- [ ] ESLint セットアップ(`eslint.config.js` Flat Config)
|
||||||
|
- [ ] Prettier セットアップ(`.prettierrc`・`.prettierignore`)
|
||||||
|
- [ ] Vitest セットアップ(`vitest.config.ts`)
|
||||||
|
- [ ] Playwright セットアップ(`playwright.config.ts`)
|
||||||
|
- [ ] Husky + lint-staged セットアップ(pre-commit で Lint・フォーマット・型チェック)
|
||||||
|
- [ ] CI 設定(`.github/workflows/ci.yml`): Lint・型チェック・Unit Test・ビルド
|
||||||
|
|
||||||
|
**package.json スクリプト**:
|
||||||
|
- [ ] `lint`・`format`・`typecheck`・`test`・`test:e2e`・`migrate`・`dev`・`build` スクリプト定義
|
||||||
|
|
||||||
|
**依存関係**:
|
||||||
|
- [ ] 本体依存: next・react・react-dom・better-sqlite3・bcrypt・react-markdown・remark-gfm・rehype-sanitize・tailwindcss
|
||||||
|
- [ ] dev依存: typescript・vitest・@playwright/test・eslint・prettier・husky・lint-staged・tsx
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] `npm run dev` で開発サーバが起動する
|
||||||
|
- [ ] `npm run lint`・`npm run typecheck`・`npm run build` が成功する
|
||||||
|
- [ ] ディレクトリ構成が `repository-structure.md` に準拠している
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M2: DB基盤(SQLラッパー・Migration)
|
||||||
|
|
||||||
|
**目的**: SQLiteへの共通アクセス基盤とSQLファイルベースのMigration機構を実装する。
|
||||||
|
|
||||||
|
**前提**: M1
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Data層**:
|
||||||
|
- [ ] `lib/db/sqlite.ts`: `SqliteDatabase` クラス実装
|
||||||
|
- `query<T>` / `get<T>` / `execute` / `transaction<T>` / `close`
|
||||||
|
- コンストラクタで `journal_mode=WAL`・`foreign_keys=ON` 設定
|
||||||
|
- `getDb()` シングルトン(dbPath = `process.env.SQLITE_PATH ?? "./data/app.db"`)
|
||||||
|
- [ ] `lib/db/migrator.ts`: `Migrator` クラス実装
|
||||||
|
- `schema_migrations` テーブル作成
|
||||||
|
- ファイル名順実行・実行済み再実行回避・1ファイル1トランザクション・失敗時ロールバック
|
||||||
|
- [ ] `lib/db/migrations/001_initial.sql`: 全16テーブル + インデックス作成
|
||||||
|
- users・projects・project_members・board_threads・board_comments・chat_messages・todo_columns・todo_items・file_assets・project_notes・milestones・calendar_events・meetings・meeting_members・notifications・activity_logs
|
||||||
|
- `idx_project_notes_project_id`・`idx_project_notes_updated_at` インデックス
|
||||||
|
|
||||||
|
**型定義**:
|
||||||
|
- [ ] `lib/types/` に全Entity型定義(User・Project・ProjectMember・BoardThread・BoardComment・ChatMessage・TodoColumn・TodoItem・FileAsset・ProjectNote・Milestone・CalendarEvent・Meeting・MeetingMember・Notification・ActivityLog・SchemaMigration)
|
||||||
|
- 列挙型: `UserRole`・`UserStatus`・`ProjectStatus`・`ProjectMemberRole`・`BoardCategory`・`TodoPriority`・`MilestoneStatus`・`CalendarEventType`・`MeetingMemberStatus`・`NotificationType`
|
||||||
|
|
||||||
|
**Migration実行・状態確認**:
|
||||||
|
- [ ] `lib/db/run-migrations.ts`: Migration実行スクリプト(`npm run migrate`)
|
||||||
|
- [ ] API: `GET /api/admin/migrations`(Migration状態確認・管理者のみ)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/lib/db/sqlite.test.ts`(query/get/execute/transaction・WAL/外部キー設定)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/lib/db/migrator.test.ts`(ファイル名順実行・再実行回避・失敗時ロールバック)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] `npm run migrate` で初期スキーマが作成される
|
||||||
|
- [ ] Migrationがファイル名順に実行され、実行済みは再実行されない
|
||||||
|
- [ ] Migration失敗時にロールバックされる
|
||||||
|
- [ ] Unit Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M3: 認証・ユーザー管理
|
||||||
|
|
||||||
|
**目的**: 独自ログイン方式による認証とユーザー管理(登録・ログイン・プロフィール・ロール・有効/無効)を実装する。
|
||||||
|
|
||||||
|
**前提**: M2
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/UserRepository.ts`: `findById`・`findByEmail`・`create`・`update`
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/AuthService.ts`: `register`・`login`・`logout`・`getCurrentUser`・`updateProfile`
|
||||||
|
- [ ] パスワードハッシュ化(bcrypt・平文保存禁止)
|
||||||
|
- [ ] ロール管理(system_admin・project_admin・member・guest)
|
||||||
|
- [ ] アカウント有効/無効(status='inactive' はログイン不可)
|
||||||
|
|
||||||
|
**認証ヘルパ**:
|
||||||
|
- [ ] `lib/auth/session.ts`: セッション読み書き
|
||||||
|
- [ ] `lib/auth/getCurrentUser.ts`: リクエストから現在ユーザー解決
|
||||||
|
|
||||||
|
**バリデータ**:
|
||||||
|
- [ ] `lib/validators/userValidator.ts`: 必須・メール形式・パスワード強度・名前長
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `POST /api/auth/register`(ユーザー登録)
|
||||||
|
- [ ] `POST /api/auth/login`(ログイン・Set-Cookie)
|
||||||
|
- [ ] `POST /api/auth/logout`(ログアウト)
|
||||||
|
- [ ] `GET /api/auth/me`(現在のユーザー)
|
||||||
|
- [ ] `PATCH /api/users/me`(プロフィール編集: 表示名・メール・アイコン画像)
|
||||||
|
- [ ] 全Route Handler に `export const runtime = 'nodejs'` 明示
|
||||||
|
- [ ] 認証ミドルウェア: 未ログイン時は保護画面をログイン画面へリダイレクト(401)
|
||||||
|
|
||||||
|
**画面**:
|
||||||
|
- [ ] `app/login/page.tsx`(ログイン画面)
|
||||||
|
- [ ] `app/profile/page.tsx`(ユーザープロフィール・アイコン画像設定・表示名設定)
|
||||||
|
- [ ] `app/layout.tsx`(ルートレイアウト)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/UserRepository.test.ts`(CRUD・email一意・論理的確認)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/AuthService.test.ts`(登録・ログイン・ログアウト・プロフィール更新・無効アカウント拒否)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/auth.spec.ts`(ログイン・ログアウト・未ログインの保護画面アクセス拒否)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] ユーザー登録・ログイン・ログアウトができる
|
||||||
|
- [ ] パスワードがハッシュ化保存される
|
||||||
|
- [ ] プロフィール・アイコン画像が編集できる
|
||||||
|
- [ ] 無効アカウントはログイン不可
|
||||||
|
- [ ] 未ログインで保護画面にアクセスできない
|
||||||
|
- [ ] Unit Test・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M4: プロジェクト管理・メンバー管理
|
||||||
|
|
||||||
|
**目的**: プロジェクトの作成・編集・削除・アーカイブと、メンバー追加・削除・ロール設定を実装する。プロジェクトダッシュボードの骨組みも作る。
|
||||||
|
|
||||||
|
**前提**: M3
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/ProjectRepository.ts`: `findById`・`findByOwner`・`create`・`update`・`delete`
|
||||||
|
- [ ] `repositories/ProjectMemberRepository.ts`: `findByProject`・`findByUser`・`add`・`remove`・`isMember`・`getRole`
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/ProjectService.ts`: `createProject`・`updateProject`・`addMember`・`removeMember`・`archiveProject`・`getDashboard`
|
||||||
|
- [ ] 権限チェック: `isMember`・`getRole`(非参加者は403・プロジェクト管理者権限チェック)
|
||||||
|
- [ ] プロジェクトステータス管理(active・on_hold・completed・archived)
|
||||||
|
- [ ] メンバー追加時に通知(M5のNotificationService連携)
|
||||||
|
|
||||||
|
**バリデータ**:
|
||||||
|
- [ ] `lib/validators/projectValidator.ts`: プロジェクト名1-200文字・説明文
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects`(自分の参加プロジェクト一覧)
|
||||||
|
- [ ] `POST /api/projects`(プロジェクト作成)
|
||||||
|
- [ ] `GET /api/projects/:projectId`(プロジェクト詳細/ダッシュボード)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId`(プロジェクト編集: 名前・説明・ステータス)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId`(プロジェクト削除)
|
||||||
|
- [ ] `GET /api/projects/:projectId/members`(メンバー一覧)
|
||||||
|
- [ ] `POST /api/projects/:projectId/members`(メンバー追加)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/members/:userId`(メンバー削除)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/dashboard/page.tsx`(個人ダッシュボード骨組み: 参加プロジェクト一覧)
|
||||||
|
- [ ] `app/projects/[projectId]/page.tsx`(プロジェクト概要/ダッシュボード骨組み)
|
||||||
|
- [ ] `app/projects/[projectId]/members/page.tsx`(メンバー管理)
|
||||||
|
- [ ] `app/projects/[projectId]/settings/page.tsx`(プロジェクト設定)
|
||||||
|
- [ ] `components/layout/`(Header・Sidebar・ProjectNav)
|
||||||
|
- [ ] `components/project/`(ProjectCard・DashboardWidget)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/ProjectRepository.test.ts`
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/ProjectMemberRepository.test.ts`(CRUD・UNIQUE制約・isMember・getRole)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/ProjectService.test.ts`(作成・編集・アーカイブ・メンバー追加/削除・権限チェック・プロジェクト分離)
|
||||||
|
- [ ] **Integration Test**: `tests/integration/project-member-permission.test.ts`(非参加者はプロジェクトデータにアクセス不可)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/project-management.spec.ts`(作成・編集・メンバー追加/削除・アーカイブ)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] プロジェクト作成・編集・削除・アーカイブができる
|
||||||
|
- [ ] メンバー追加・削除・ロール設定ができる
|
||||||
|
- [ ] 非参加者はプロジェクト情報にアクセスできない(403)
|
||||||
|
- [ ] Unit・Integration・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M5: 通知・アクティビティログ基盤
|
||||||
|
|
||||||
|
**目的**: 通知生成とアクティビティログ記録の基盤Serviceを実装し、後続機能(掲示板・チャット・ToDo等)から利用可能にする。
|
||||||
|
|
||||||
|
**前提**: M4
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/NotificationRepository.ts`: 通知作成・未読一覧(ページネーション)・既読化
|
||||||
|
- [ ] `repositories/ActivityLogRepository.ts`: ログ作成・プロジェクト別一覧(ページネーション)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/NotificationService.ts`: `notifyOnEvent`・`resolveTargets`
|
||||||
|
- 対象イベント: mention・todo_assigned・todo_due_soon・meeting_invited・board_commented・project_added・file_shared・note_updated
|
||||||
|
- 対象ユーザー解決ロジック(メンション先・担当者・ミーティング参加者・掲示板投稿者・プロジェクトメンバー等)
|
||||||
|
- [ ] `services/ActivityLogService.ts`: `logActivity`
|
||||||
|
- 記録対象: todo_created・todo_updated・todo_completed・file_uploaded・board_posted・comment_added・note_created・note_updated・meeting_created・member_added・milestone_updated
|
||||||
|
- [ ] 管理者操作もアクティビティログに記録
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/notifications`(未読通知一覧)
|
||||||
|
- [ ] `POST /api/notifications/:id/read`(既読化)
|
||||||
|
- [ ] `GET /api/projects/:projectId/activity`(アクティビティログ一覧)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/notifications/page.tsx`(通知一覧画面)※共通画面
|
||||||
|
- [ ] `components/notifications/NotificationList.tsx`・`NotificationBadge.tsx`(ヘッダの未読バッジ)
|
||||||
|
- [ ] `app/projects/[projectId]/activity/page.tsx`(プロジェクト別アクティビティログ画面)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/NotificationRepository.test.ts`
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/ActivityLogRepository.test.ts`
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/NotificationService.test.ts`(正しいユーザーへ通知作成・resolveTargets)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/ActivityLogService.test.ts`(ログ記録・プロジェクト分離)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] 各イベント種別で通知が正しいユーザーに作成される
|
||||||
|
- [ ] 変更操作がアクティビティログに記録される
|
||||||
|
- [ ] 通知の既読化ができる
|
||||||
|
- [ ] Unit Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M6: 掲示板
|
||||||
|
|
||||||
|
**目的**: プロジェクト単位の非リアルタイム情報共有(スレッド・コメント・カテゴリ・ピン留め・重要マーク・既読・検索)を実装する。
|
||||||
|
|
||||||
|
**前提**: M5
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/BoardRepository.ts`: スレッドCRUD・コメントCRUD・検索・ページネーション(`deleted_at IS NULL` 必須)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/BoardService.ts`: スレッド作成/編集/削除・コメント作成/編集/削除
|
||||||
|
- [ ] 権限チェック(プロジェクト参加者のみ)
|
||||||
|
- [ ] カテゴリ分類(notice・spec・minutes・question・decision・trouble・memo)
|
||||||
|
- [ ] ピン留め・重要マーク
|
||||||
|
- [ ] 既読管理
|
||||||
|
- [ ] コメント追加時に通知(`board_commented` → 投稿者)・アクティビティログ記録(`board_posted`・`comment_added`)
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/board/threads`(一覧・ページネーション・検索)
|
||||||
|
- [ ] `POST /api/projects/:projectId/board/threads`(作成)
|
||||||
|
- [ ] `GET /api/projects/:projectId/board/threads/:threadId`(詳細)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/board/threads/:threadId`(編集)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/board/threads/:threadId`(削除)
|
||||||
|
- [ ] `POST /api/projects/:projectId/board/threads/:threadId/comments`(コメント作成)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/board/comments/:commentId`(コメント編集)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/board/comments/:commentId`(コメント削除)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/board/page.tsx`(掲示板一覧)
|
||||||
|
- [ ] スレッド詳細・作成/編集フォーム
|
||||||
|
- [ ] `components/board/`(ThreadList・ThreadForm・CommentList)
|
||||||
|
- [ ] Markdown本文表示(react-markdown + rehype-sanitize)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/BoardRepository.test.ts`(CRUD・論理削除非取得・プロジェクト分離・検索)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/BoardService.test.ts`(権限チェック・カテゴリ・ピン留め・通知・アクティビティログ)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/board.spec.ts`(スレッド作成・編集・コメント・検索)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] スレッド・コメントのCRUDができる
|
||||||
|
- [ ] カテゴリ・ピン留め・重要マーク・既読・検索ができる
|
||||||
|
- [ ] 論理削除済みデータが通常取得に含まれない
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M7: Markdownメモ
|
||||||
|
|
||||||
|
**目的**: プロジェクトごとのMarkdownメモ(作成・編集・プレビュー・タグ・ピン留め・検索・添付・関連付け)を実装する。
|
||||||
|
|
||||||
|
**前提**: M5
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/ProjectNoteRepository.ts`: メモCRUD・検索・ピン留め(`deleted_at IS NULL`・インデックス活用)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/NoteService.ts`: メモ作成/編集/削除
|
||||||
|
- [ ] 権限チェック
|
||||||
|
- [ ] タイトル・タグ・ピン留め
|
||||||
|
- [ ] 作成者・最終更新者・更新日時管理
|
||||||
|
- [ ] ファイル添付・関連ToDo・関連ミーティング設定
|
||||||
|
- [ ] 更新時に通知(`note_updated`)・アクティビティログ(`note_created`・`note_updated`)
|
||||||
|
|
||||||
|
**Markdownレンダリング(セキュリティ)**:
|
||||||
|
- [ ] react-markdown + remark-gfm + rehype-sanitize でプレビュー
|
||||||
|
- [ ] HTML直接入力無効化・危険URLスキーム(`javascript:`等)除外
|
||||||
|
- [ ] 対応記法: 見出し・箇条書き・番号リスト・チェックリスト・コードブロック・テーブル・リンク・画像・引用
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/notes`(一覧・検索・ページネーション)
|
||||||
|
- [ ] `POST /api/projects/:projectId/notes`(作成)
|
||||||
|
- [ ] `GET /api/projects/:projectId/notes/:noteId`(詳細)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/notes/:noteId`(編集)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/notes/:noteId`(削除)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/notes/page.tsx`(メモ一覧)
|
||||||
|
- [ ] メモエディタ・プレビュー画面
|
||||||
|
- [ ] `components/notes/`(NoteEditor・MarkdownPreview)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/ProjectNoteRepository.test.ts`(CRUD・検索・ピン留め・論理削除・プロジェクト分離)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/NoteService.test.ts`(権限・タグ・ピン留め・関連付け・通知・アクティビティログ)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/markdown-notes.spec.ts`(作成・編集・プレビュー・ピン留め・検索・削除)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] MarkdownメモのCRUD・プレビューができる
|
||||||
|
- [ ] タグ・ピン留め・検索ができる
|
||||||
|
- [ ] MarkdownがサニタイズされHTML直接入力が無効化される
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M8: SSE基盤・チャット
|
||||||
|
|
||||||
|
**目的**: SSE配信基盤(SseHub)とプロジェクト別リアルタイムチャット(送信・編集・削除・メンション・リアクション・既読/未読・検索)を実装する。
|
||||||
|
|
||||||
|
**前提**: M5
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**SSE基盤**:
|
||||||
|
- [ ] `lib/sse/hub.ts`: `SseHub` クラス(`addClient`・`removeClient`・`broadcast`)
|
||||||
|
- プロジェクト単位のクライアント集合管理
|
||||||
|
- 当該プロジェクトのみ配信(他プロジェクトへ漏れさせない)
|
||||||
|
- [ ] SSEエンドポイント: `GET /api/projects/:projectId/chat/stream`
|
||||||
|
- [ ] クライアント側の自動再接続対応
|
||||||
|
- [ ] SSEイベント種別: `chat.message.created`・`chat.message.updated`・`chat.message.deleted`・`todo.updated`・`file.uploaded`・`meeting.created`・`note.updated`・`notification.created`
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/ChatRepository.ts`: メッセージCRUD・ページネーション・検索(`deleted_at IS NULL`)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/ChatService.ts`: `sendMessage`・`editMessage`・`deleteMessage`・`getHistory`
|
||||||
|
- [ ] 権限チェック(プロジェクト参加者のみ)
|
||||||
|
- [ ] メンション検出 → 通知(`mention`)
|
||||||
|
- [ ] SSE配信(`chat.message.created/updated/deleted`)
|
||||||
|
- [ ] リアクション・既読/未読・添付ファイル
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/chat/messages`(履歴・ページネーション・検索)
|
||||||
|
- [ ] `POST /api/projects/:projectId/chat/messages`(送信)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/chat/messages/:messageId`(編集)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/chat/messages/:messageId`(削除)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/chat/page.tsx`(チャット画面)
|
||||||
|
- [ ] `components/chat/`(ChatWindow・MessageInput・MessageList)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/lib/sse/hub.test.ts`(クライアント管理・プロジェクト別配信・配信スコープ)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/ChatRepository.test.ts`(CRUD・検索・論理削除・プロジェクト分離)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/ChatService.test.ts`(送信・編集・削除・メンション通知・SSE配信呼出・権限)
|
||||||
|
- [ ] **Integration Test**: `tests/integration/chat-sse-broadcast.test.ts`(送信→SSE配信)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/chat-sse.spec.ts`(送信・別コンテキストでSSEリアルタイム受信・編集・削除)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] メッセージ送信・編集・削除ができる
|
||||||
|
- [ ] SSEで別クライアントにリアルタイム配信される
|
||||||
|
- [ ] 接続切断時に自動再接続される
|
||||||
|
- [ ] メンション・リアクション・既読/未読・検索ができる
|
||||||
|
- [ ] Unit・Integration・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M9: ToDo / Kanban
|
||||||
|
|
||||||
|
**目的**: プロジェクトごとのタスクをKanban形式で管理する(カラム・タスク・ドラッグ&ドロップ・担当者・期限・優先度・ラベル・チェックリスト・完了・マイルストーン紐づけ・カレンダー表示)。
|
||||||
|
|
||||||
|
**前提**: M8
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/TodoRepository.ts`: カラムCRUD・タスクCRUD・並び替え(orderIndex再計算)・ページネーション(`deleted_at IS NULL`)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/TodoService.ts`: カラム作成/編集/削除/並び替え・タスク作成/編集/削除/移動
|
||||||
|
- [ ] 権限チェック
|
||||||
|
- [ ] 担当者・期限・優先度(low/normal/high)・ラベル設定
|
||||||
|
- [ ] チェックリスト・コメント・添付ファイル
|
||||||
|
- [ ] 完了状態管理(`completedAt`)
|
||||||
|
- [ ] マイルストーン紐づけ
|
||||||
|
- [ ] 担当者割り当て時に通知(`todo_assigned`)・アクティビティログ(`todo_created`・`todo_updated`・`todo_completed`)
|
||||||
|
- [ ] SSE配信(`todo.updated`)
|
||||||
|
- [ ] 標準カラム初期生成: Backlog・To Do・In Progress・Review・Done
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/todos/columns`(カラム一覧)
|
||||||
|
- [ ] `POST /api/projects/:projectId/todos/columns`(カラム作成)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/todos/columns/:columnId`(カラム編集/並び替え)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/todos/columns/:columnId`(カラム削除)
|
||||||
|
- [ ] `GET /api/projects/:projectId/todos/items`(タスク一覧)
|
||||||
|
- [ ] `POST /api/projects/:projectId/todos/items`(タスク作成)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/todos/items/:itemId`(タスク編集/移動)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/todos/items/:itemId`(タスク削除)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/todos/page.tsx`(Kanbanボード)
|
||||||
|
- [ ] `components/todo/`(KanbanBoard・KanbanColumn・TodoCard)ドラッグ&ドロップ対応
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/TodoRepository.test.ts`(CRUD・並び替え・論理削除・プロジェクト分離)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/TodoService.test.ts`(作成・編集・移動・完了・担当者通知・アクティビティログ・権限)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/todo-kanban.spec.ts`(カラム作成・タスク作成・編集・別カラム移動・担当者/期限設定・完了)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] Kanbanボードが表示される(標準カラム5つ)
|
||||||
|
- [ ] カラム・タスクのCRUD・ドラッグ&ドロップ移動ができる
|
||||||
|
- [ ] 担当者・期限・優先度・ラベル・チェックリスト・コメントが設定できる
|
||||||
|
- [ ] 完了タスクがDoneカラムに表示される
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M10: ファイル共有・Lightbox
|
||||||
|
|
||||||
|
**目的**: プロジェクト内ファイルのアップロード・一覧・フォルダ管理・Lightbox閲覧・PDFプレビュー・紐づけを実装する。
|
||||||
|
|
||||||
|
**前提**: M4
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/FileRepository.ts`: ファイルメタCRUD・フォルダ管理(`deleted_at IS NULL`)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/FileStorageService.ts`: `upload`・`getDownloadStream`・`delete`
|
||||||
|
- ローカルFS保存: `data/uploads/<projectId>/<uuid>.<ext>`
|
||||||
|
- MIMEタイプチェック・ファイル名サニタイズ・保存名一意化
|
||||||
|
- ファイルアクセス権限チェック(プロジェクト参加者のみ)
|
||||||
|
- アクティビティログ(`file_uploaded`)・通知(`file_shared`)・SSE配信(`file.uploaded`)
|
||||||
|
|
||||||
|
**ファイル紐づけ**:
|
||||||
|
- [ ] ファイルとToDo・掲示板投稿・ミーティング・Markdownメモの紐づけ
|
||||||
|
- [ ] ファイルコメント
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/files`(一覧・ページネーション)
|
||||||
|
- [ ] `POST /api/projects/:projectId/files`(アップロード・multipart)
|
||||||
|
- [ ] `GET /api/files/:fileId/download`(ダウンロード・権限チェック)
|
||||||
|
- [ ] `DELETE /api/files/:fileId`(削除)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/files/page.tsx`(ファイル一覧)
|
||||||
|
- [ ] `components/files/`(FileList・Uploader・Lightbox)
|
||||||
|
- 画像Lightbox表示・PDFプレビュー
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/FileRepository.test.ts`
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/FileStorageService.test.ts`(アップロード・MIMEチェック・保存名一意化・権限・削除・アクティビティログ)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/file-sharing.spec.ts`(アップロード・一覧・Lightbox閲覧・PDFプレビュー・削除)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] ファイルアップロード・一覧・ダウンロード・削除ができる
|
||||||
|
- [ ] 画像をLightboxで閲覧できる・PDFをプレビューできる
|
||||||
|
- [ ] MIMEチェック・権限チェックが機能する
|
||||||
|
- [ ] ファイル紐づけ・コメントができる
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M11: カレンダー・マイルストーン
|
||||||
|
|
||||||
|
**目的**: カレンダー(月/週/日/リスト表示・各種イベント表示・フィルター)とマイルストーン管理(CRUD・進捗率自動計算)を実装する。
|
||||||
|
|
||||||
|
**前提**: M9
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/CalendarRepository.ts`: イベントCRUD・期間検索(`deleted_at IS NULL`)
|
||||||
|
- [ ] `repositories/MilestoneRepository.ts`: マイルストーンCRUD・関連ToDo取得(`deleted_at IS NULL`)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/ScheduleService.ts`: `getCalendarEvents`(期間・フィルター)
|
||||||
|
- 表示対象: マイルストーン・デッドライン・ToDo開始日・ToDo期限・ミーティング・任意イベント
|
||||||
|
- メンバー別フィルター・種別フィルター
|
||||||
|
- [ ] マイルストーン管理Service: 作成/編集/削除・期限・説明文・関連ToDo紐づけ
|
||||||
|
- [ ] **進捗率自動計算アルゴリズム**: 関連ToDoの完了率から0-100を算出
|
||||||
|
- [ ] 完了状態管理・カレンダー表示
|
||||||
|
- [ ] アクティビティログ(`milestone_updated`)
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/calendar/events`(イベント一覧・期間・フィルタ)
|
||||||
|
- [ ] `POST /api/projects/:projectId/calendar/events`(イベント作成)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/calendar/events/:eventId`(編集)
|
||||||
|
- [ ] `DELETE /api/projects/:projectId/calendar/events/:eventId`(削除)
|
||||||
|
- [ ] `GET /api/projects/:projectId/milestones`(マイルストーン一覧)
|
||||||
|
- [ ] `POST /api/projects/:projectId/milestones`(作成)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/milestones/:id`(編集)
|
||||||
|
- [ ] `GET /api/projects/:projectId/milestones/:id/progress`(進捗率取得)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/calendar/page.tsx`(カレンダー: 月/週/日/リスト表示)
|
||||||
|
- [ ] `app/projects/[projectId]/milestones/page.tsx`(マイルストーン一覧・進捗バー)
|
||||||
|
- [ ] `components/calendar/`(CalendarView・EventBadge)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/CalendarRepository.test.ts`
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/MilestoneRepository.test.ts`
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/ScheduleService.test.ts`(期間検索・フィルタ・種別別取得)
|
||||||
|
- [ ] **Unit Test**: 進捗率計算アルゴリズム(`calcMilestoneProgress`・ToDo0件時0%・完了率計算)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/calendar.spec.ts`(ToDo期限・マイルストーン・ミーティング表示・イベント作成/編集)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] 月/週/日/リスト表示ができる
|
||||||
|
- [ ] マイルストーン・デッドライン・ToDo期限・ミーティングが表示される
|
||||||
|
- [ ] イベントCRUD・フィルターができる
|
||||||
|
- [ ] マイルストーン進捗率が自動計算される
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M12: ミーティング管理・スケジュール重複チェック
|
||||||
|
|
||||||
|
**目的**: ミーティングCRUD・参加メンバー設定・アジェンダ/議事録・関連付け・スケジュール重複判定を実装する。
|
||||||
|
|
||||||
|
**前提**: M11
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Repository**:
|
||||||
|
- [ ] `repositories/MeetingRepository.ts`: ミーティングCRUD・meeting_members管理(`deleted_at IS NULL`)
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/MeetingService.ts`: `createMeeting`・`updateMeeting`・`deleteMeeting`・`checkScheduleConflicts`・`updateMinutes`
|
||||||
|
- [ ] 権限チェック
|
||||||
|
- [ ] タイトル・説明・開始/終了日時・参加メンバー・場所・ミーティングURL設定
|
||||||
|
- [ ] アジェンダ・議事録(Markdown)
|
||||||
|
- [ ] 関連ToDo・関連ファイル・関連掲示板投稿・関連Markdownメモ設定
|
||||||
|
- [ ] カレンダー表示連携
|
||||||
|
- [ ] 参加者招待通知(`meeting_invited`)・アクティビティログ(`meeting_created`)・SSE配信(`meeting.created`)
|
||||||
|
|
||||||
|
**スケジュール重複判定アルゴリズム**:
|
||||||
|
- [ ] `checkScheduleConflicts`: 選択メンバーの予定重複を検出
|
||||||
|
- 判定対象1: 他のミーティング(meeting_members経由・時間重複)
|
||||||
|
- 判定対象2: カレンダーイベント(時間重複)
|
||||||
|
- 判定対象3: 期限の近い重要タスク(priority='high'・dueDate ±3日以内)
|
||||||
|
- 時間重複判定: `NOT (existing.end <= new.start OR existing.start >= new.end)`
|
||||||
|
- 重複があれば警告返却(作成自体はブロックしない)
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/projects/:projectId/meetings`(一覧)
|
||||||
|
- [ ] `POST /api/projects/:projectId/meetings`(作成・conflicts返却)
|
||||||
|
- [ ] `PATCH /api/projects/:projectId/meetings/:id`(編集)
|
||||||
|
- [ ] `POST /api/projects/:projectId/meetings/check`(予定重複チェックのみ)
|
||||||
|
|
||||||
|
**画面・コンポーネント**:
|
||||||
|
- [ ] `app/projects/[projectId]/meetings/page.tsx`(ミーティング一覧)
|
||||||
|
- [ ] ミーティング作成フォーム・アジェンダ/議事録編集
|
||||||
|
- [ ] `components/meetings/`(MeetingForm・ConflictWarning)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/repositories/MeetingRepository.test.ts`(CRUD・meeting_members・UNIQUE制約・論理削除・プロジェクト分離)
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/MeetingService.test.ts`(作成・編集・議事録更新・参加者通知・アクティビティログ・権限)
|
||||||
|
- [ ] **Unit Test**: スケジュール重複判定アルゴリズム(他ミーティング重複・カレンダーイベント重複・重要タスク検出・時間重複ロジック・excludeMeetingId)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/meetings.spec.ts`(作成・参加メンバー設定・予定重複警告・アジェンダ/議事録・関連付け)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] ミーティングCRUD・参加メンバー設定ができる
|
||||||
|
- [ ] アジェンダ・議事録が入力できる
|
||||||
|
- [ ] 関連付け(ToDo/ファイル/掲示板/メモ)ができる
|
||||||
|
- [ ] 参加メンバーの予定重複が画面上で警告される
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M13: 検索・ダッシュボード完成
|
||||||
|
|
||||||
|
**目的**: プロジェクト内横断検索と、個人/プロジェクトダッシュボードの全項目表示を完成させる。
|
||||||
|
|
||||||
|
**前提**: M6-M12(各データソース完成後)
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**横断検索**:
|
||||||
|
- [ ] 検索対象: 掲示板・チャット・ToDo・ファイル名・カレンダーイベント・ミーティング・議事録・マイルストーン・Markdownメモ
|
||||||
|
- [ ] 検索条件: キーワード・投稿者・担当者・日付・種別・プロジェクト・タグ
|
||||||
|
- [ ] API: `GET /api/projects/:projectId/search`
|
||||||
|
- [ ] 画面: 検索結果一覧
|
||||||
|
|
||||||
|
**個人ダッシュボード完成**:
|
||||||
|
- [ ] 自分の参加プロジェクト
|
||||||
|
- [ ] 自分の未完了ToDo
|
||||||
|
- [ ] 今日の予定
|
||||||
|
- [ ] 近日中のミーティング
|
||||||
|
- [ ] 未読通知
|
||||||
|
- [ ] 期限切れタスク
|
||||||
|
- [ ] 最近のアクティビティ
|
||||||
|
- [ ] `app/dashboard/page.tsx` 完成版
|
||||||
|
|
||||||
|
**プロジェクトダッシュボード完成**:
|
||||||
|
- [ ] プロジェクト概要
|
||||||
|
- [ ] 進行中ToDo
|
||||||
|
- [ ] 期限が近いToDo(7日以内)
|
||||||
|
- [ ] 最新チャット(直近5件)
|
||||||
|
- [ ] 最新掲示板(ピン留め優先・直近5件)
|
||||||
|
- [ ] 最新Markdownメモ(ピン留め優先・直近5件)
|
||||||
|
- [ ] 最近のファイル(直近5件)
|
||||||
|
- [ ] 次回ミーティング(直近1件)
|
||||||
|
- [ ] マイルストーン進捗(進捗バー付き)
|
||||||
|
- [ ] 最近のアクティビティ(直近10件)
|
||||||
|
- [ ] `app/projects/[projectId]/page.tsx` 完成版
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: 検索ロジック(各リソース横断検索・フィルタ絞り込み)
|
||||||
|
- [ ] **Unit Test**: ダッシュボード集計ロジック(未完了ToDo・期限近いToDo・今日の予定等)
|
||||||
|
- [ ] **E2E Test**: ダッシュボード表示確認(個人・プロジェクト各項目の表示)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] 横断検索で全リソースが検索できる
|
||||||
|
- [ ] 検索フィルターが機能する
|
||||||
|
- [ ] 個人ダッシュボードの全項目が表示される
|
||||||
|
- [ ] プロジェクトダッシュボードの全項目が表示される
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M14: バックアップ・管理者機能
|
||||||
|
|
||||||
|
**目的**: 管理者によるバックアップ作成(DB+uploads ZIP化)・一覧・ダウンロードとMigration状態確認画面を実装する。
|
||||||
|
|
||||||
|
**前提**: M2
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Service**:
|
||||||
|
- [ ] `services/BackupService.ts`: `createBackup`・`listBackups`・`downloadBackup`
|
||||||
|
- SQLite DBファイル + uploadsディレクトリをZIP化
|
||||||
|
- 保存先: `backups/backup-<timestamp>.zip`
|
||||||
|
- 管理者権限チェック(`role='system_admin'` のみ)
|
||||||
|
- 管理者操作をアクティビティログに記録
|
||||||
|
|
||||||
|
**API**:
|
||||||
|
- [ ] `GET /api/admin/backups`(バックアップ一覧・管理者のみ)
|
||||||
|
- [ ] `POST /api/admin/backups`(バックアップ作成・管理者のみ)
|
||||||
|
- [ ] `GET /api/admin/backups/:filename`(ダウンロード・管理者のみ)
|
||||||
|
- [ ] `GET /api/admin/migrations`(Migration状態確認・管理者のみ)※M2でAPI実装済なら画面連携
|
||||||
|
|
||||||
|
**画面**:
|
||||||
|
- [ ] `app/admin/backups/page.tsx`(管理者バックアップ画面: 作成・一覧・ダウンロード)
|
||||||
|
- [ ] Migration状態確認画面(管理者)
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
- [ ] **Unit Test**: `tests/unit/services/BackupService.test.ts`(バックアップ作成・ZIP化・一覧・ダウンロード・権限チェック)
|
||||||
|
- [ ] **E2E Test**: `tests/e2e/backup.spec.ts`(作成・一覧表示・ダウンロード)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] 管理者がバックアップを作成できる
|
||||||
|
- [ ] バックアップ一覧が表示される
|
||||||
|
- [ ] バックアップファイルをダウンロードできる
|
||||||
|
- [ ] 非管理者はアクセスできない(403)
|
||||||
|
- [ ] Migration状態が確認できる
|
||||||
|
- [ ] Unit・E2E Test が成功する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M15: 全体テスト完成・品質担保
|
||||||
|
|
||||||
|
**目的**: 全マイルストールのテストを統合し、カバレッジ目標と成功条件/受け入れ要件を全項目クリアする。
|
||||||
|
|
||||||
|
**前提**: M1-M14
|
||||||
|
|
||||||
|
### 実装タスク
|
||||||
|
|
||||||
|
**Unit Test 完成**:
|
||||||
|
- [ ] SQLラッパー・Migration・全13Repository・全12Service のUnit Test 完成
|
||||||
|
- [ ] 権限チェック・バリデーション・スケジュール重複判定・通知作成ロジック・アクティビティログ作成ロジック・マイルストーン進捗計算 のUnit Test 完成
|
||||||
|
- [ ] Repository/Service層カバレッジ 80%以上
|
||||||
|
- [ ] `npm test` 全件成功
|
||||||
|
|
||||||
|
**統合テスト完成**:
|
||||||
|
- [ ] `tests/integration/auth-flow.test.ts`(登録→ログイン→保護画面アクセス)
|
||||||
|
- [ ] `tests/integration/project-member-permission.test.ts`(プロジェクト作成→メンバー追加→権限分離)
|
||||||
|
- [ ] `tests/integration/chat-sse-broadcast.test.ts`(チャット送信→SSE配信)
|
||||||
|
|
||||||
|
**E2E Test 完成**:
|
||||||
|
- [ ] 全12シナリオ完成: auth・project-management・board・chat-sse・todo-kanban・file-sharing・markdown-notes・calendar・meetings・notifications・activity-log・backup
|
||||||
|
- [ ] 主要フローカバレッジ 100%
|
||||||
|
- [ ] `npm run test:e2e` 全件成功
|
||||||
|
|
||||||
|
**品質ゲート**:
|
||||||
|
- [ ] `npm run lint` エラーなし
|
||||||
|
- [ ] `npm run typecheck` 成功
|
||||||
|
- [ ] `npm run build` 成功
|
||||||
|
|
||||||
|
**受け入れ要件確認**:
|
||||||
|
- [ ] PRD「成功条件/受け入れ要件」の全項目を確認(ユーザーログイン〜バックアップ作成・Unit Test・E2E Test 全成功まで)
|
||||||
|
|
||||||
|
### 完了条件
|
||||||
|
- [ ] `npm test` でUnit Testが全件成功する
|
||||||
|
- [ ] `npm run test:e2e` でPlaywright E2E Testが全件成功する
|
||||||
|
- [ ] 統合テストが全件成功する
|
||||||
|
- [ ] Lint・型チェック・ビルドが成功する
|
||||||
|
- [ ] PRDの受け入れ要件を全項目クリアする
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 機能カバレッジマトリクス
|
||||||
|
|
||||||
|
PRDの機能要件(16機能)+DB基盤+テストがどのマイルストーンで実装されるかを示す。機能漏れがないことを保証する。
|
||||||
|
|
||||||
|
| 機能 | PRD優先度 | 実装MS | 完了確認MS | 備考 |
|
||||||
|
|------|----------|--------|-----------|------|
|
||||||
|
| DB基盤(SQLラッパー・Migration・Repository基盤) | P0 | M2 | M2 | 全16テーブル+schema_migrations |
|
||||||
|
| ユーザー管理(登録・ログイン・プロフィール・ロール・有効/無効) | P0 | M3 | M3 | bcrypt・独自ログイン |
|
||||||
|
| プロジェクト管理(作成・編集・削除・アーカイブ・ステータス) | P0 | M4 | M4 | |
|
||||||
|
| プロジェクトメンバー管理(追加・削除・ロール) | P0 | M4 | M4 | |
|
||||||
|
| 通知(アプリ内・未読一覧・既読化・8イベント) | P0 | M5 | M5 | 基盤Service・各機能から連携 |
|
||||||
|
| アクティビティログ(11操作記録・時系列表示) | P1 | M5 | M5 | 基盤Service・各機能から連携 |
|
||||||
|
| 掲示板(スレッド・コメント・カテゴリ・ピン留め・重要・既読・検索) | P0 | M6 | M6 | |
|
||||||
|
| Markdownメモ(CRUD・プレビュー・タグ・ピン留め・検索・添付・関連付け) | P0 | M7 | M7 | サニタイズ必須 |
|
||||||
|
| SSE基盤(SseHub・プロジェクト別配信・自動再接続・8イベント) | P0 | M8 | M8 | |
|
||||||
|
| チャット(送信・編集・削除・メンション・リアクション・既読/未読・検索) | P0 | M8 | M8 | SSEリアルタイム |
|
||||||
|
| ToDo / Kanban(カラム・タスク・D&D・担当・期限・優先度・ラベル・チェックリスト・完了・マイルストーン紐づけ) | P0 | M9 | M9 | |
|
||||||
|
| ファイル共有(アップロード・一覧・フォルダ・Lightbox・PDF・コメント・紐づけ・MIMEチェック) | P0 | M10 | M10 | ローカルFS保存 |
|
||||||
|
| カレンダー(月/週/日/リスト・イベントCRUD・フィルタ) | P0 | M11 | M11 | |
|
||||||
|
| マイルストーン管理(CRUD・期限・関連ToDo・進捗率自動計算・完了) | P1 | M11 | M11 | |
|
||||||
|
| ミーティング管理(CRUD・メンバー・アジェンダ/議事録・関連付け・カレンダー連携) | P0 | M12 | M12 | |
|
||||||
|
| スケジュール重複チェック(他ミーティング・カレンダーイベント・重要タスク) | P0 | M12 | M12 | 警告表示(ブロックしない) |
|
||||||
|
| 検索(横断検索・7フィルタ条件) | P1 | M13 | M13 | 9リソース横断 |
|
||||||
|
| 個人ダッシュボード(7項目) | P0 | M4(骨組み) | M13 | 全項目完成はM13 |
|
||||||
|
| プロジェクトダッシュボード(9項目) | P0 | M4(骨組み) | M13 | 全項目完成はM13 |
|
||||||
|
| バックアップ(DB+uploads ZIP・一覧・ダウンロード・Migration状態確認) | P1 | M14 | M14 | 管理者のみ |
|
||||||
|
|
||||||
|
### Repository カバレッジ(13クラス)
|
||||||
|
|
||||||
|
| Repository | 実装MS | Unit Test MS |
|
||||||
|
|-----------|--------|-------------|
|
||||||
|
| UserRepository | M3 | M3 |
|
||||||
|
| ProjectRepository | M4 | M4 |
|
||||||
|
| ProjectMemberRepository | M4 | M4 |
|
||||||
|
| NotificationRepository | M5 | M5 |
|
||||||
|
| ActivityLogRepository | M5 | M5 |
|
||||||
|
| BoardRepository | M6 | M6 |
|
||||||
|
| ProjectNoteRepository | M7 | M7 |
|
||||||
|
| ChatRepository | M8 | M8 |
|
||||||
|
| TodoRepository | M9 | M9 |
|
||||||
|
| FileRepository | M10 | M10 |
|
||||||
|
| CalendarRepository | M11 | M11 |
|
||||||
|
| MilestoneRepository | M11 | M11 |
|
||||||
|
| MeetingRepository | M12 | M12 |
|
||||||
|
|
||||||
|
### Service カバレッジ(12クラス)
|
||||||
|
|
||||||
|
| Service | 実装MS | Unit Test MS |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| AuthService | M3 | M3 |
|
||||||
|
| ProjectService | M4 | M4 |
|
||||||
|
| NotificationService | M5 | M5 |
|
||||||
|
| ActivityLogService | M5 | M5 |
|
||||||
|
| BoardService | M6 | M6 |
|
||||||
|
| NoteService | M7 | M7 |
|
||||||
|
| ChatService | M8 | M8 |
|
||||||
|
| TodoService | M9 | M9 |
|
||||||
|
| FileStorageService | M10 | M10 |
|
||||||
|
| ScheduleService | M11 | M11 |
|
||||||
|
| (マイルストーン進捗計算) | M11 | M11 |
|
||||||
|
| MeetingService | M12 | M12 |
|
||||||
|
| (スケジュール重複判定) | M12 | M12 |
|
||||||
|
| BackupService | M14 | M14 |
|
||||||
|
|
||||||
|
### E2E Test カバレッジ(12シナリオ)
|
||||||
|
|
||||||
|
| E2Eシナリオ | 実装MS |
|
||||||
|
|------------|--------|
|
||||||
|
| auth.spec.ts | M3 |
|
||||||
|
| project-management.spec.ts | M4 |
|
||||||
|
| board.spec.ts | M6 |
|
||||||
|
| markdown-notes.spec.ts | M7 |
|
||||||
|
| chat-sse.spec.ts | M8 |
|
||||||
|
| todo-kanban.spec.ts | M9 |
|
||||||
|
| file-sharing.spec.ts | M10 |
|
||||||
|
| calendar.spec.ts | M11 |
|
||||||
|
| meetings.spec.ts | M12 |
|
||||||
|
| notifications.spec.ts | M5(通知一覧)/ 各MS(イベント発生時) |
|
||||||
|
| activity-log.spec.ts | M5(一覧)/ 各MS(記録時) |
|
||||||
|
| backup.spec.ts | M14 |
|
||||||
|
|
||||||
|
### 統合テスト カバレッジ(3シナリオ)
|
||||||
|
|
||||||
|
| 統合テスト | 実装MS |
|
||||||
|
|----------|--------|
|
||||||
|
| auth-flow.test.ts | M3 |
|
||||||
|
| project-member-permission.test.ts | M4 |
|
||||||
|
| chat-sse-broadcast.test.ts | M8 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## フェーズ対応表
|
||||||
|
|
||||||
|
PRDの開発順序(Phase 1〜5)とマイルストーンの対応。
|
||||||
|
|
||||||
|
| フェーズ | 内容 | 対象マイルストーン |
|
||||||
|
|---------|------|------------------|
|
||||||
|
| Phase 1: 基盤 | Next.js・Tailwind・SQLite・SQLラッパー・Migration・Repository基盤・認証・ユーザー管理・プロジェクト管理・メンバー管理 | M1・M2・M3・M4 |
|
||||||
|
| Phase 2: プロジェクト内基本機能 | 通知・アクティビティログ・プロジェクトダッシュボード(骨組み)・掲示板・Markdownメモ | M5・M6・M7 |
|
||||||
|
| Phase 3: リアルタイム・タスク管理 | SSE基盤・チャット・メンション・既読/未読・ToDo/Kanban | M8・M9 |
|
||||||
|
| Phase 4: ファイル・カレンダー | ファイル共有・Lightbox・カレンダー・マイルストーン・デッドライン表示 | M10・M11 |
|
||||||
|
| Phase 5: ミーティング・バックアップ | ミーティング作成・メンバー設定・スケジュール重複チェック・議事録・ToDo連携・メモ連携・バックアップ | M12・M14 |
|
||||||
|
| (横断) | 検索・ダッシュボード完成 | M13 |
|
||||||
|
| (全体) | 全体テスト完成・品質担保 | M15 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## テスト要件サマリー
|
||||||
|
|
||||||
|
`docs/development-guidelines.md` の「テスト実装の必須条件」に準拠し、各マイルストーンでテスト実装を必須とする。
|
||||||
|
|
||||||
|
### Unit Test(Vitest)【必須】
|
||||||
|
- **対象**: SQLラッパー・Migration・全13Repository・全12Service・権限チェック・バリデーション・スケジュール重複判定・通知作成ロジック・アクティビティログ作成ロジック・マイルストーン進捗計算
|
||||||
|
- **合格基準**: `npm test` 全件成功・Repository/Service層カバレッジ80%以上・正常系/異常系網羅
|
||||||
|
- **最終確認**: M15
|
||||||
|
|
||||||
|
### 統合テスト(Vitest)【必須】
|
||||||
|
- **対象**: 認証フロー・プロジェクトメンバー権限・チャットSSE配信
|
||||||
|
- **最終確認**: M15
|
||||||
|
|
||||||
|
### E2E Test(Playwright)【必須】
|
||||||
|
- **対象**: 認証・プロジェクト管理・掲示板・チャット(SSE)・ToDo(Kanban)・ファイル共有(Lightbox)・Markdownメモ・カレンダー・ミーティング(予定重複警告)・通知・アクティビティログ・バックアップ(全12シナリオ)
|
||||||
|
- **合格基準**: `npm run test:e2e` 全件成功・主要フロー100%
|
||||||
|
- **最終確認**: M15
|
||||||
524
docs/product-requirements.md
Normal file
524
docs/product-requirements.md
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
# Product Requirements Document
|
||||||
|
|
||||||
|
## プロダクト概要
|
||||||
|
|
||||||
|
### 名前
|
||||||
|
**シンプルグループウェア** - プロジェクト単位で情報共有・タスク管理を行えるチームコラボレーションツール
|
||||||
|
|
||||||
|
### プロダクトコンセプト
|
||||||
|
- **プロジェクト中心の情報管理**: すべての主要機能(掲示板・チャット・ToDo・ファイル・メモ・カレンダー・ミーティング)がプロジェクトに紐づき、プロジェクトごとに独立して利用できる
|
||||||
|
- **軽量で扱いやすい技術構成**: Next.js 15 + TypeScript + SQLite(better-sqlite3) で構築し、Prisma を使わず独自SQLラッパーとRepositoryクラスでDBアクセスを実装する。小規模チーム(プロジェクトあたり数十人)で運用できるシンプルさを追求する
|
||||||
|
- **リアルタイム性と一元管理の両立**: SSEによるリアルタイムチャットを備えつつ、カレンダーでマイルストーン・デッドライン・ToDo・ミーティングを一画面に集約し、チームの進捗を可視化する
|
||||||
|
|
||||||
|
### プロダクトビジョン
|
||||||
|
プロジェクトごとに独立した情報共有空間を提供し、チームが掲示板・チャット・タスク・ファイル・メモ・カレンダー・ミーティングを一つのアプリで完結できるグループウェアを実現する。
|
||||||
|
軽量なSQLiteベースの構成により、小規模チームでも簡単に導入・運用できる。
|
||||||
|
リアルタイムチャットとカレンダーによる一元管理で、情報散逸を防ぎ、チームの生産性を高める。
|
||||||
|
|
||||||
|
### 目的
|
||||||
|
- ユーザーを管理できる
|
||||||
|
- プロジェクトを作成・管理できる
|
||||||
|
- プロジェクトごとにメンバーを管理できる
|
||||||
|
- プロジェクトごとに掲示板を使える
|
||||||
|
- プロジェクトごとにSSEチャットを使える
|
||||||
|
- プロジェクトごとにKanban形式のToDoを使える
|
||||||
|
- プロジェクトごとにファイル共有を使える
|
||||||
|
- ファイルをLightboxで閲覧できる
|
||||||
|
- プロジェクトごとにMarkdownメモを作成・編集・閲覧できる
|
||||||
|
- プロジェクトごとにカレンダーを使える
|
||||||
|
- カレンダーでマイルストーン、デッドライン、ToDo、ミーティングを表示できる
|
||||||
|
- プロジェクトごとにミーティングを管理できる
|
||||||
|
- ミーティング作成時に参加メンバーの予定重複を確認できる
|
||||||
|
- アプリ内通知を使える
|
||||||
|
- アクティビティログを記録できる
|
||||||
|
- SQLiteのDBスキーマをSQL Migrationで更新できる
|
||||||
|
- 管理者がバックアップを作成できる
|
||||||
|
|
||||||
|
## ターゲットユーザー
|
||||||
|
|
||||||
|
### 主要ペルソナ: 佐藤 健太(32歳、プロジェクトマネージャー)
|
||||||
|
- 中小規模の開発チーム(5〜30名)で複数プロジェクトを並行管理している
|
||||||
|
- プロジェクトごとにメンバー構成が変わり、情報共有手段を都度切り替えるのに負担を感じている
|
||||||
|
- チャット・タスク管理・ファイル共有・ミーティング調整が別々のツールに分散しており、情報を行き来させるのに時間がかかっている
|
||||||
|
- 各メンバーの予定やタスク期限を一画面で把握し、ミーティングの重複を自動検知したい
|
||||||
|
- 軽量なツールを自社サーバー(またはローカル)で手軽に運用したい
|
||||||
|
|
||||||
|
### サブペルソナ: 山田 花子(27歳、フロントエンドエンジニア / プロジェクトメンバー)
|
||||||
|
- 複数プロジェクトに所属し、それぞれのToDo期限やミーティング予定を見逃さずに把握したい
|
||||||
|
- Markdownでメモや議事録を書くことに慣れており、プレビュー付きの編集環境を求めている
|
||||||
|
- チャットでリアルタイムに相談しつつ、掲示板で非同期的に仕様や決定事項を残したい
|
||||||
|
|
||||||
|
## 成功指標(KPI)
|
||||||
|
|
||||||
|
### 主要KPI
|
||||||
|
- **主要ユーザーフローの動作確認**: 認証・プロジェクト作成・掲示板・SSEチャット・Kanban ToDo・ファイル共有・Markdownメモ・カレンダー・ミーティング・通知・アクティビティログ・バックアップのすべてが正常に動作する(リリース時)
|
||||||
|
- **Unit Test カバレッジ**: SQLラッパー・Migration・全Repository・全ServiceクラスのUnit Testが実装され、`npm test` で全件成功する(リリース時)
|
||||||
|
- **E2E Test 合格率**: Playwrightによる主要ユーザーフローのE2E Testが実装され、`npm run test:e2e` で全件成功する(リリース時)
|
||||||
|
|
||||||
|
### 副次KPI
|
||||||
|
- **リアルタイム性**: チャットメッセージ送信から他クライアントのSSE受信まで1秒以内(小規模同時接続前提)
|
||||||
|
- **一覧表示の応答性**: チャット履歴・掲示板・ファイル一覧・Markdownメモ一覧はページネーションされ、1ページあたりの取得が1秒以内
|
||||||
|
- **バックアップの可用性**: 管理者が1操作でSQLite DBファイルとuploadsディレクトリをZIP化したバックアップを作成・ダウンロードできる
|
||||||
|
|
||||||
|
## 機能要件
|
||||||
|
|
||||||
|
### コア機能(MVP)
|
||||||
|
|
||||||
|
#### 1. ユーザー管理
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
システム管理者として、ユーザーの登録・ロール割り当て・有効/無効管理を行いたい。それはシステムへのアクセスを適切に制御するためである。
|
||||||
|
またユーザーとして、パスワードでログインし、自分のプロフィール・アイコン・表示名を編集したい。それは自分のアイデンティティをチーム内で正しく表現するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] ユーザー登録ができる(名前・メールアドレス・パスワード)
|
||||||
|
- [ ] パスワードログインができる
|
||||||
|
- [ ] ログアウトができる
|
||||||
|
- [ ] プロフィール編集(表示名・メールアドレス)ができる
|
||||||
|
- [ ] アイコン画像を設定できる
|
||||||
|
- [ ] アカウントを有効/無効にできる(無効アカウントはログイン不可)
|
||||||
|
- [ ] ロール(System Admin / Project Admin / Member / Guest)を管理できる
|
||||||
|
- [ ] 認証必須の画面で未ログイン時はログイン画面へリダイレクトされる
|
||||||
|
- [ ] ログインしていないユーザーは保護された画面にアクセスできない
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. プロジェクト管理
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクト管理者として、プロジェクトを作成し、メンバーを追加・削除し、プロジェクト内ロールを設定したい。それはプロジェクトごとに適切な権限でチームを運用するためである。
|
||||||
|
またプロジェクト管理者として、プロジェクトのステータス(Active/On Hold/Completed/Archived)を管理し、不要になったプロジェクトをアーカイブ・削除したい。それはプロジェクトのライフサイクルを整理するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] プロジェクトを作成できる(名前・説明文)
|
||||||
|
- [ ] プロジェクト名・説明文を編集できる
|
||||||
|
- [ ] プロジェクトのステータス(Active/On Hold/Completed/Archived)を変更できる
|
||||||
|
- [ ] プロジェクトにメンバーを追加できる
|
||||||
|
- [ ] プロジェクトからメンバーを削除できる
|
||||||
|
- [ ] プロジェクト内ロールを設定できる
|
||||||
|
- [ ] プロジェクトをアーカイブできる
|
||||||
|
- [ ] プロジェクトを削除できる
|
||||||
|
- [ ] プロジェクト参加者以外はプロジェクト情報にアクセスできない
|
||||||
|
- [ ] プロジェクトダッシュボードに以下が表示される:
|
||||||
|
- 最新チャット
|
||||||
|
- 最新掲示板投稿
|
||||||
|
- 最新Markdownメモ
|
||||||
|
- 未完了ToDo
|
||||||
|
- 期限が近いToDo
|
||||||
|
- 次回ミーティング
|
||||||
|
- 直近のマイルストーン
|
||||||
|
- 最近アップロードされたファイル
|
||||||
|
- 最近のアクティビティ
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. 掲示板
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクト単位で非リアルタイムの情報共有(お知らせ・仕様・議事録・質問・決定事項・トラブル・メモ)をスレッド形式で行いたい。それは重要な情報を後から振り返れる形で残すためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] スレッドを作成・編集・削除できる(Markdown本文)
|
||||||
|
- [ ] スレッドにコメントを作成・編集・削除できる
|
||||||
|
- [ ] スレッドに添付ファイルを付けられる
|
||||||
|
- [ ] スレッドをピン留めできる
|
||||||
|
- [ ] スレッドに重要マークを付けられる
|
||||||
|
- [ ] カテゴリ(お知らせ/仕様/議事録/質問/決定事項/トラブル/メモ)で分類できる
|
||||||
|
- [ ] 既読管理ができる
|
||||||
|
- [ ] スレッドを検索できる
|
||||||
|
- [ ] 論理削除されたスレッド・コメントは通常取得に含まれない
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 4. チャット(SSE)
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクト内でリアルタイムに短い会話をしたい。それは素早い相談ややり取りを円滑に行うためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] プロジェクト別のチャットが使える
|
||||||
|
- [ ] メッセージを送信できる
|
||||||
|
- [ ] SSEにより新規メッセージがリアルタイムに他クライアントへ配信される(別ブラウザ/別コンテキストで確認)
|
||||||
|
- [ ] 接続切断時に自動再接続される
|
||||||
|
- [ ] SSEイベントはプロジェクト単位で配信される
|
||||||
|
- [ ] メッセージを編集・削除できる
|
||||||
|
- [ ] メッセージに添付ファイルを付けられる
|
||||||
|
- [ ] メンションができる
|
||||||
|
- [ ] リアクションができる
|
||||||
|
- [ ] 既読/未読が管理できる
|
||||||
|
- [ ] チャット履歴を検索できる(ページネーション付き)
|
||||||
|
- [ ] SSEイベント種別: `chat.message.created` / `chat.message.updated` / `chat.message.deleted` / `todo.updated` / `file.uploaded` / `meeting.created` / `note.updated` / `notification.created` が配信される
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 5. ToDo / Kanban
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクトごとのタスクをKanbanボード形式で管理したい。それはタスクの状態と担当を視覚的に把握するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] Kanbanボードが表示される(標準カラム: Backlog/To Do/In Progress/Review/Done)
|
||||||
|
- [ ] カラムを作成・編集・削除・並び替えできる
|
||||||
|
- [ ] タスクを作成・編集・削除できる
|
||||||
|
- [ ] タスクをドラッグ&ドロップで別カラムへ移動できる
|
||||||
|
- [ ] タスクに担当者・期限・優先度・ラベルを設定できる
|
||||||
|
- [ ] タスクにチェックリスト・コメント・添付ファイルを付けられる
|
||||||
|
- [ ] タスクの完了状態を管理できる
|
||||||
|
- [ ] 完了したToDoがDoneカラムに表示される
|
||||||
|
- [ ] タスクをカレンダーに表示できる
|
||||||
|
- [ ] タスクをマイルストーンと紐づけられる
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 6. ファイル共有
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクト内でファイルをアップロード・閲覧・共有したい。それは資料や画像をチーム内で簡単に扱うためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] ファイルをアップロードできる(ローカルファイルシステムに保存)
|
||||||
|
- [ ] ファイル一覧が表示される(ページネーション付き)
|
||||||
|
- [ ] フォルダ管理ができる
|
||||||
|
- [ ] ファイル名変更・削除・ダウンロードができる
|
||||||
|
- [ ] 画像ファイルをLightboxで閲覧できる
|
||||||
|
- [ ] PDFファイルをプレビューできる
|
||||||
|
- [ ] ファイルにコメントを付けられる
|
||||||
|
- [ ] ファイルをToDo・掲示板投稿・ミーティング・Markdownメモと紐づけられる
|
||||||
|
- [ ] ファイルアクセスにも権限チェックが行われる
|
||||||
|
- [ ] アップロードファイルのMIMEチェックが行われる
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 7. Markdownメモ
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクトごとにMarkdown形式のメモを作成・編集・閲覧したい。それは仕様メモやノウハウを構造化して蓄積するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] Markdownメモを作成・編集・削除できる
|
||||||
|
- [ ] Markdownプレビューが表示される
|
||||||
|
- [ ] タイトル・タグを設定できる
|
||||||
|
- [ ] メモをピン留めできる
|
||||||
|
- [ ] メモを検索できる
|
||||||
|
- [ ] 作成者・最終更新者・更新日時が表示される
|
||||||
|
- [ ] ファイル添付ができる
|
||||||
|
- [ ] 関連ToDo・関連ミーティングを設定できる
|
||||||
|
- [ ] 対応記法: 見出し・箇条書き・番号リスト・チェックリスト・コードブロック・テーブル・リンク・画像・引用
|
||||||
|
- [ ] HTML直接入力は無効化され、表示時にサニタイズされる
|
||||||
|
- [ ] 危険なURLスキームは除外される
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 8. カレンダー
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクト内の予定・マイルストーン・ToDo期限・ミーティングをカレンダー上で確認したい。それはチームのスケジュールを一画面で把握するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] 月表示・週表示・日表示・リスト表示ができる
|
||||||
|
- [ ] マイルストーン・デッドライン・ToDo開始日・ToDo期限・ミーティング・任意イベントが表示される
|
||||||
|
- [ ] カレンダーイベントを作成・編集・削除できる
|
||||||
|
- [ ] イベント種別(Meeting/Deadline/Milestone/Todo/Reminder/Custom)を設定できる
|
||||||
|
- [ ] メンバー別フィルターができる
|
||||||
|
- [ ] 種別フィルターができる
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 9. マイルストーン管理
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクト管理者として、プロジェクトの重要な節目(マイルストーン)を管理したい。それは進捗の目標を明確にし、完了率を可視化するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] マイルストーンを作成・編集・削除できる
|
||||||
|
- [ ] 期限・説明文を設定できる
|
||||||
|
- [ ] 関連ToDoを紐づけられる
|
||||||
|
- [ ] 関連ToDoの完了率から進捗率が自動計算される
|
||||||
|
- [ ] 完了状態を管理できる
|
||||||
|
- [ ] カレンダーに表示される
|
||||||
|
|
||||||
|
**優先度**: P1(重要)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 10. ミーティング管理
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクト管理者として、プロジェクトメンバーとのミーティングを設定し、参加者の予定重複を確認したい。それはスケジュールの競合を事前に防ぐためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] ミーティングを作成・編集・削除できる
|
||||||
|
- [ ] タイトル・説明・開始日時・終了日時・参加メンバー・場所・ミーティングURLを設定できる
|
||||||
|
- [ ] アジェンダ・議事録をMarkdownで作成できる
|
||||||
|
- [ ] 関連ToDo・関連ファイル・関連掲示板投稿・関連Markdownメモを設定できる
|
||||||
|
- [ ] カレンダーに表示される
|
||||||
|
- [ ] ミーティング作成時に参加メンバーの予定重複をチェックし、同じ時間帯に他のミーティング・カレンダーイベント・期限の近い重要タスクがある場合は警告する
|
||||||
|
- [ ] 参加メンバーの予定重複が画面上で警告される
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 11. 通知
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
ユーザーとして、自分に関係する重要な更新(メンション・ToDo割り当て・期限前・ミーティング招待・コメント・プロジェクト追加・ファイル共有・メモ更新)を見逃さないようにしたい。それは必要な対応を迅速に行うためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] アプリ内通知が表示される
|
||||||
|
- [ ] 未読通知一覧が表示される
|
||||||
|
- [ ] 通知を既読化できる
|
||||||
|
- [ ] 以下のイベントで通知が作成される:
|
||||||
|
- 自分がメンションされた
|
||||||
|
- 自分にToDoが割り当てられた
|
||||||
|
- ToDoの期限が近い
|
||||||
|
- ミーティングに招待された
|
||||||
|
- 掲示板にコメントが付いた
|
||||||
|
- プロジェクトに追加された
|
||||||
|
- ファイルが共有された
|
||||||
|
- Markdownメモが更新された
|
||||||
|
- [ ] 通知が正しいユーザーに作成される
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 12. アクティビティログ
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクト内の変更履歴を時系列で確認したい。それは誰がいつ何を変更したかを追跡するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] 以下の操作がアクティビティログに記録される:
|
||||||
|
- ToDo作成・更新・完了
|
||||||
|
- ファイルアップロード
|
||||||
|
- 掲示板投稿
|
||||||
|
- コメント追加
|
||||||
|
- Markdownメモ作成・更新
|
||||||
|
- ミーティング作成
|
||||||
|
- メンバー追加
|
||||||
|
- マイルストーン更新
|
||||||
|
- [ ] アクティビティログがプロジェクト単位で時系列表示される
|
||||||
|
- [ ] 管理者操作もアクティビティログに記録される
|
||||||
|
|
||||||
|
**優先度**: P1(重要)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 13. 検索
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
プロジェクトメンバーとして、プロジェクト内の情報(掲示板・チャット・ToDo・ファイル・カレンダーイベント・ミーティング・議事録・マイルストーン・Markdownメモ)を横断検索したい。それは目的の情報にすばやくたどり着くためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] 掲示板・チャット・ToDo・ファイル名・カレンダーイベント・ミーティング・議事録・マイルストーン・Markdownメモを検索できる
|
||||||
|
- [ ] 検索条件(キーワード・投稿者・担当者・日付・種別・プロジェクト・タグ)で絞り込みできる
|
||||||
|
|
||||||
|
**優先度**: P1(重要)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 14. ダッシュボード
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
ユーザーとして、ログイン後に自分に関係する情報を一画面で確認したい。それは今日やるべきことと近日の予定を把握するためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] 個人ダッシュボードに以下が表示される:
|
||||||
|
- 自分の参加プロジェクト
|
||||||
|
- 自分の未完了ToDo
|
||||||
|
- 今日の予定
|
||||||
|
- 近日中のミーティング
|
||||||
|
- 未読通知
|
||||||
|
- 期限切れタスク
|
||||||
|
- 最近のアクティビティ
|
||||||
|
- [ ] プロジェクトダッシュボードに以下が表示される:
|
||||||
|
- プロジェクト概要
|
||||||
|
- 進行中ToDo
|
||||||
|
- 期限が近いToDo
|
||||||
|
- 最新チャット
|
||||||
|
- 最新掲示板
|
||||||
|
- 最新Markdownメモ
|
||||||
|
- 最近のファイル
|
||||||
|
- 次回ミーティング
|
||||||
|
- マイルストーン進捗
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 15. バックアップ
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
システム管理者として、SQLite DBファイルとアップロードファイルをバックアップしたい。それはデータ喪失に備えるためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] 管理者用バックアップ画面が使える
|
||||||
|
- [ ] SQLite DBファイルのバックアップを作成できる
|
||||||
|
- [ ] uploadsディレクトリのバックアップを作成できる
|
||||||
|
- [ ] DBとuploadsをZIP化できる
|
||||||
|
- [ ] バックアップファイル一覧が表示される
|
||||||
|
- [ ] バックアップファイルをダウンロードできる
|
||||||
|
|
||||||
|
**優先度**: P1(重要)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 16. DB基盤(SQLラッパー・Migration・Repository)
|
||||||
|
|
||||||
|
**ユーザーストーリー**:
|
||||||
|
開発者として、SQLiteへのアクセスを共通SQLラッパーで行い、SQLファイルベースのMigrationでスキーマを管理したい。それはDBアクセスを一貫させ、スキーマ変更を追跡可能にするためである。
|
||||||
|
|
||||||
|
**受け入れ基準**:
|
||||||
|
- [ ] 共通SQLラッパー(SELECT複数行/SELECT単一行/INSERT・UPDATE・DELETE/トランザクション/初期設定/接続管理/エラー処理)が実装される
|
||||||
|
- [ ] 各Repositoryは直接SQLiteライブラリを触らず、共通SQLラッパーを通してSQLを実行する
|
||||||
|
- [ ] Migrationファイルがファイル名順に実行される
|
||||||
|
- [ ] 実行済みMigrationは再実行されない
|
||||||
|
- [ ] 実行済み履歴は `schema_migrations` テーブルに保存される
|
||||||
|
- [ ] 1ファイルごとにトランザクションが張られ、失敗時はロールバックされる
|
||||||
|
- [ ] 管理者がMigration状態を確認できる
|
||||||
|
- [ ] SQLはパラメータバインドで実行される(SQLインジェクション対策)
|
||||||
|
|
||||||
|
**優先度**: P0(必須)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 画面構成
|
||||||
|
|
||||||
|
#### 共通画面
|
||||||
|
- ログイン画面
|
||||||
|
- ダッシュボード
|
||||||
|
- 通知一覧
|
||||||
|
- ユーザープロフィール
|
||||||
|
- 管理者設定
|
||||||
|
- バックアップ管理
|
||||||
|
|
||||||
|
#### プロジェクト内画面
|
||||||
|
- プロジェクト概要
|
||||||
|
- 掲示板
|
||||||
|
- チャット
|
||||||
|
- ToDo / Kanban
|
||||||
|
- ファイル
|
||||||
|
- Markdownメモ
|
||||||
|
- カレンダー
|
||||||
|
- マイルストーン
|
||||||
|
- ミーティング
|
||||||
|
- メンバー
|
||||||
|
- アクティビティログ
|
||||||
|
- 設定
|
||||||
|
|
||||||
|
### 今後の機能(Post-MVP)
|
||||||
|
|
||||||
|
#### 外部連携・拡張
|
||||||
|
- 外部カレンダー(Google Calendar等)との同期
|
||||||
|
- メール通知・Webhook通知
|
||||||
|
- モバイル専用UIの最適化
|
||||||
|
|
||||||
|
**優先度**: P2(今後検討)
|
||||||
|
|
||||||
|
## 非機能要件
|
||||||
|
|
||||||
|
### パフォーマンス
|
||||||
|
- 1プロジェクトあたり数十人規模を想定する
|
||||||
|
- チャットSSEは小規模同時接続を前提とする
|
||||||
|
- ファイル一覧・チャット履歴・掲示板・Markdownメモはページネーションを行う
|
||||||
|
- 一覧取得では必要な件数だけ取得する
|
||||||
|
|
||||||
|
### ユーザビリティ
|
||||||
|
- ログイン後、ユーザーはダッシュボードで自分に関係する情報を一画面で確認できる
|
||||||
|
- プロジェクト内の主要機能はサイドメニューから1クリックで遷移できる
|
||||||
|
- Kanbanタスクはドラッグ&ドロップで直感的に操作できる
|
||||||
|
|
||||||
|
### 信頼性
|
||||||
|
- DB更新は1ファイルごとにトランザクションを張り、失敗時はロールバックする
|
||||||
|
- 管理者がSQLite DBファイルとuploadsディレクトリのバックアップを作成できる
|
||||||
|
- SQLite DBファイル・uploadsディレクトリは永続化される
|
||||||
|
|
||||||
|
### セキュリティ
|
||||||
|
- 認証必須(未ログインは保護された画面にアクセス不可)
|
||||||
|
- プロジェクト参加者以外はプロジェクト情報にアクセス不可
|
||||||
|
- ファイルアクセスにも権限チェックを行う
|
||||||
|
- SQLはパラメータバインドで実行する
|
||||||
|
- Markdown表示時はHTMLを無効化しサニタイズする
|
||||||
|
- 危険なURLスキームを除外する
|
||||||
|
- アップロードファイルのMIMEチェックを行う
|
||||||
|
- 管理者操作をアクティビティログに記録する
|
||||||
|
|
||||||
|
### スケーラビリティ
|
||||||
|
- Next.jsのEdge Runtimeは使用せず、API Route・Server Actions・DBアクセスはすべてNode.js Runtimeで実行する(SQLite直接扱いのため)
|
||||||
|
- SQLiteはWALモード・外部キー制約ONで運用する
|
||||||
|
|
||||||
|
### 運用・保守
|
||||||
|
- `.env` で設定管理できる
|
||||||
|
- SQLite DBファイルを永続化する
|
||||||
|
- uploadsディレクトリを永続化する
|
||||||
|
- 管理者がDB Migration状態を確認できる
|
||||||
|
- 管理者がバックアップを作成できる
|
||||||
|
|
||||||
|
## テスト要件
|
||||||
|
|
||||||
|
### Unit Test(Vitest)
|
||||||
|
以下を対象とする:
|
||||||
|
- SQLラッパー
|
||||||
|
- Migration実行
|
||||||
|
- 全Repository(User/Project/ProjectMember/Board/Chat/Todo/File/Calendar/Meeting/ProjectNote/Notification/ActivityLog)
|
||||||
|
- 全Service(Auth/Project/Chat/Meeting/Schedule/FileStorage/Backup)
|
||||||
|
- 権限チェック・バリデーション・スケジュール重複判定・通知作成ロジック・アクティビティログ作成ロジック
|
||||||
|
|
||||||
|
確認内容:
|
||||||
|
- 正常にデータを作成・取得・更新・削除できる
|
||||||
|
- 論理削除されたデータが通常取得に含まれない
|
||||||
|
- プロジェクト単位でデータが分離される
|
||||||
|
- 権限がないユーザーは操作できない
|
||||||
|
- 必須項目不足時にエラーになる
|
||||||
|
- Migrationがファイル名順に実行され、実行済みは再実行されない
|
||||||
|
- 予定重複を検出できる
|
||||||
|
- 通知が正しいユーザーに作成される
|
||||||
|
- アクティビティログが正しく記録される
|
||||||
|
|
||||||
|
`npm test` でUnit Testが実行できること。
|
||||||
|
|
||||||
|
### E2E Test(Playwright)
|
||||||
|
主要ユーザーフローを実際のブラウザ操作で検証する:
|
||||||
|
- 認証(ログイン・ログアウト・未ログインの保護)
|
||||||
|
- プロジェクト管理(作成・編集・メンバー追加/削除・アーカイブ)
|
||||||
|
- 掲示板(スレッド作成・編集・コメント・検索)
|
||||||
|
- チャット(送信・SSEリアルタイム受信・編集・削除)
|
||||||
|
- ToDo/Kanban(カラム作成・タスク作成・編集・移動・担当者/期限設定・完了)
|
||||||
|
- ファイル共有(アップロード・一覧・Lightbox閲覧・PDFプレビュー・削除)
|
||||||
|
- Markdownメモ(作成・編集・プレビュー・ピン留め・検索・削除)
|
||||||
|
- カレンダー(ToDo期限・マイルストーン・ミーティング表示・イベント作成/編集)
|
||||||
|
- ミーティング(作成・参加メンバー設定・予定重複警告・アジェンダ/議事録・関連付け)
|
||||||
|
- 通知(ToDo担当者・メンション・ミーティング参加者への通知・既読化)
|
||||||
|
- アクティビティログ(ToDo作成・ファイルアップロード・メモ更新・ミーティング作成の記録)
|
||||||
|
- バックアップ(作成・一覧表示・ダウンロード)
|
||||||
|
|
||||||
|
`npm run test:e2e` でPlaywright E2E Testが実行できること。
|
||||||
|
Unit TestとE2E Testがすべて成功すること。
|
||||||
|
|
||||||
|
## スコープ外
|
||||||
|
|
||||||
|
明示的にスコープ外とする項目:
|
||||||
|
- Prisma等のORMは使用しない(独自SQLラッパー + Repositoryクラスで実装)
|
||||||
|
- Next.js Edge Runtimeは使用しない(Node.js Runtimeのみ)
|
||||||
|
- 外部カレンダー(Google Calendar等)との同期(Post-MVP)
|
||||||
|
- メール通知・Webhook通知(Post-MVP)
|
||||||
|
- モバイル専用UIの最適化(Post-MVP)
|
||||||
|
- 外部認証プロバイダ(OAuth/SSO)は使用しない(独自ログイン方式)
|
||||||
|
- クラウドストレージへのファイル保存は行わない(ローカルファイルシステムのみ)
|
||||||
506
docs/repository-structure.md
Normal file
506
docs/repository-structure.md
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
# リポジトリ構成定義書 (Repository Structure Document)
|
||||||
|
|
||||||
|
> 本書は `docs/architecture.md` で定義したレイヤードアーキテクチャを具体的なディレクトリ構成に落とし込んだものである。Next.js 15 App Router の規約に従い、`app/`・`lib/`・`repositories/`・`services/`・`components/` をルート直下に配置する。
|
||||||
|
|
||||||
|
## プロジェクト構成
|
||||||
|
|
||||||
|
```
|
||||||
|
repo/
|
||||||
|
├── app/ # UI層: Next.js App Router
|
||||||
|
│ ├── api/ # Route Handlers (REST/SSEエンドポイント)
|
||||||
|
│ ├── login/ # ログイン画面
|
||||||
|
│ ├── dashboard/ # 個人ダッシュボード
|
||||||
|
│ ├── projects/[projectId]/ # プロジェクト内画面群
|
||||||
|
│ ├── profile/ # ユーザープロフィール
|
||||||
|
│ ├── admin/ # 管理者画面(バックアップ等)
|
||||||
|
│ ├── layout.tsx # ルートレイアウト
|
||||||
|
│ └── globals.css # Tailwind エントリ
|
||||||
|
├── lib/ # Data層 + 共通基盤
|
||||||
|
│ ├── db/ # SQLite接続・Migration
|
||||||
|
│ ├── sse/ # SSE配信基盤
|
||||||
|
│ ├── auth/ # 認証・セッションヘルパ
|
||||||
|
│ ├── types/ # Entity型定義
|
||||||
|
│ └── validators/ # 入力バリデーション
|
||||||
|
├── repositories/ # Repository層
|
||||||
|
├── services/ # Service層
|
||||||
|
├── components/ # Reactコンポーネント
|
||||||
|
│ ├── layout/
|
||||||
|
│ ├── project/
|
||||||
|
│ ├── board/
|
||||||
|
│ ├── chat/
|
||||||
|
│ ├── todo/
|
||||||
|
│ ├── files/
|
||||||
|
│ ├── calendar/
|
||||||
|
│ ├── meetings/
|
||||||
|
│ ├── notes/
|
||||||
|
│ └── notifications/
|
||||||
|
├── tests/ # テストコード
|
||||||
|
│ ├── unit/
|
||||||
|
│ ├── integration/
|
||||||
|
│ └── e2e/
|
||||||
|
├── data/ # 永続化データ(git管理外)
|
||||||
|
│ ├── app.db
|
||||||
|
│ └── uploads/
|
||||||
|
├── backups/ # バックアップZIP(git管理外)
|
||||||
|
├── docs/ # プロジェクトドキュメント
|
||||||
|
├── .steering/ # ワーク単位のステアリングファイル
|
||||||
|
├── .opencode/ # opencode設定
|
||||||
|
├── public/ # 静的アセット
|
||||||
|
└── 設定ファイル群(package.json, tsconfig.json, next.config 等)
|
||||||
|
```
|
||||||
|
|
||||||
|
## ディレクトリ詳細
|
||||||
|
|
||||||
|
### app/ (UI層)
|
||||||
|
|
||||||
|
**役割**: Next.js App Router。画面表示・入力受付・認証・認可・Route Handler/SSEエンドポイント。
|
||||||
|
|
||||||
|
**配置ファイル**:
|
||||||
|
- `page.tsx`: 画面コンポーネント(Server Components 中心)
|
||||||
|
- `route.ts`: Route Handler(REST API)
|
||||||
|
- `layout.tsx`: レイアウト
|
||||||
|
- `loading.tsx` / `error.tsx`: ローディング・エラー状態
|
||||||
|
|
||||||
|
**命名規則**:
|
||||||
|
- ディレクトリはNext.js規約(`[projectId]`等の動的セグメント含む)
|
||||||
|
- 画面ディレクトリは kebab-case(`projects/[projectId]/board/`)
|
||||||
|
|
||||||
|
**依存**:
|
||||||
|
- 依存可能: `services/`, `components/`, `lib/`
|
||||||
|
- 依存禁止: `repositories/`, `lib/db/` への直接アクセス(Service経由のみ)
|
||||||
|
|
||||||
|
**構成例**:
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── api/
|
||||||
|
│ ├── auth/
|
||||||
|
│ │ ├── login/route.ts
|
||||||
|
│ │ ├── logout/route.ts
|
||||||
|
│ │ └── me/route.ts
|
||||||
|
│ ├── projects/
|
||||||
|
│ │ ├── route.ts # 一覧・作成
|
||||||
|
│ │ └── [projectId]/
|
||||||
|
│ │ ├── route.ts # 詳細・編集・削除
|
||||||
|
│ │ ├── members/route.ts
|
||||||
|
│ │ ├── board/
|
||||||
|
│ │ ├── chat/
|
||||||
|
│ │ │ ├── messages/route.ts
|
||||||
|
│ │ │ └── stream/route.ts # SSEエンドポイント
|
||||||
|
│ │ ├── todos/
|
||||||
|
│ │ ├── files/
|
||||||
|
│ │ ├── notes/
|
||||||
|
│ │ ├── calendar/
|
||||||
|
│ │ ├── milestones/
|
||||||
|
│ │ └── meetings/
|
||||||
|
│ ├── files/[fileId]/download/route.ts
|
||||||
|
│ ├── notifications/route.ts
|
||||||
|
│ └── admin/
|
||||||
|
│ ├── backups/route.ts
|
||||||
|
│ └── migrations/route.ts
|
||||||
|
├── login/page.tsx
|
||||||
|
├── dashboard/page.tsx
|
||||||
|
├── projects/[projectId]/
|
||||||
|
│ ├── page.tsx # プロジェクト概要/ダッシュボード
|
||||||
|
│ ├── board/page.tsx
|
||||||
|
│ ├── chat/page.tsx
|
||||||
|
│ ├── todos/page.tsx
|
||||||
|
│ ├── files/page.tsx
|
||||||
|
│ ├── notes/page.tsx
|
||||||
|
│ ├── calendar/page.tsx
|
||||||
|
│ ├── milestones/page.tsx
|
||||||
|
│ ├── meetings/page.tsx
|
||||||
|
│ ├── members/page.tsx
|
||||||
|
│ ├── activity/page.tsx
|
||||||
|
│ └── settings/page.tsx
|
||||||
|
├── profile/page.tsx
|
||||||
|
└── admin/backups/page.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
**各 Route Handler の先頭に `export const runtime = 'nodejs'` を明示する**(Edge Runtime使用禁止)。
|
||||||
|
|
||||||
|
### lib/ (Data層 + 共通基盤)
|
||||||
|
|
||||||
|
#### lib/db/
|
||||||
|
|
||||||
|
**役割**: SQLite接続の共通管理・SQL実行・Migration。
|
||||||
|
|
||||||
|
**配置ファイル**:
|
||||||
|
- `sqlite.ts`: `SqliteDatabase` クラス・`getDb()` シングルトン
|
||||||
|
- `migrator.ts`: `Migrator` クラス
|
||||||
|
- `migrations/*.sql`: Migrationファイル(ファイル名順実行)
|
||||||
|
|
||||||
|
**命名規則**: Migrationファイルは `001_initial.sql` 形式(3桁連番 + 説明)
|
||||||
|
|
||||||
|
**依存**: better-sqlite3。Repository層からのみ利用される。
|
||||||
|
|
||||||
|
#### lib/sse/
|
||||||
|
|
||||||
|
**役割**: プロジェクト単位のSSEクライアント管理・イベント配信。
|
||||||
|
|
||||||
|
**配置ファイル**:
|
||||||
|
- `hub.ts`: `SseHub` クラス(addClient/removeClient/broadcast)
|
||||||
|
|
||||||
|
**依存**: なし(Service層から呼び出される)。
|
||||||
|
|
||||||
|
#### lib/auth/
|
||||||
|
|
||||||
|
**役割**: セッション管理・認証ヘルパ・現在ユーザー取得。
|
||||||
|
|
||||||
|
**配置ファイル**:
|
||||||
|
- `session.ts`: セッション読み書き
|
||||||
|
- `getCurrentUser.ts`: リクエストから現在ユーザー解決
|
||||||
|
|
||||||
|
#### lib/types/
|
||||||
|
|
||||||
|
**役割**: Entity型定義・共有型。`functional-design.md` のデータモデルに対応。
|
||||||
|
|
||||||
|
**配置ファイル**: `User.ts`, `Project.ts`, `BoardThread.ts`, `ChatMessage.ts`, `TodoItem.ts`, `FileAsset.ts`, `ProjectNote.ts`, `Milestone.ts`, `CalendarEvent.ts`, `Meeting.ts`, `Notification.ts`, `ActivityLog.ts` 等
|
||||||
|
|
||||||
|
**命名規則**: PascalCase(Entity名)
|
||||||
|
|
||||||
|
#### lib/validators/
|
||||||
|
|
||||||
|
**役割**: 入力バリデーション(必須・長さ・形式)。Service層から呼び出される。
|
||||||
|
|
||||||
|
**配置ファイル**: `projectValidator.ts`, `todoValidator.ts`, `meetingValidator.ts` 等
|
||||||
|
|
||||||
|
**命名規則**: camelCase + `Validator` 接尾
|
||||||
|
|
||||||
|
### repositories/ (Repository層)
|
||||||
|
|
||||||
|
**役割**: SQLの保持・パラメータバインド実行・論理削除フィルタ(`deleted_at IS NULL`)・プロジェクト分離の担保。直接SQLiteライブラリを触らず `lib/db/sqlite.ts` 経由。
|
||||||
|
|
||||||
|
**配置ファイル**: テーブルごとに1クラス。
|
||||||
|
|
||||||
|
```
|
||||||
|
repositories/
|
||||||
|
├── UserRepository.ts
|
||||||
|
├── ProjectRepository.ts
|
||||||
|
├── ProjectMemberRepository.ts
|
||||||
|
├── BoardRepository.ts
|
||||||
|
├── ChatRepository.ts
|
||||||
|
├── TodoRepository.ts
|
||||||
|
├── FileRepository.ts
|
||||||
|
├── CalendarRepository.ts
|
||||||
|
├── MeetingRepository.ts
|
||||||
|
├── ProjectNoteRepository.ts
|
||||||
|
├── NotificationRepository.ts
|
||||||
|
├── ActivityLogRepository.ts
|
||||||
|
└── MilestoneRepository.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: PascalCase + `Repository` 接尾
|
||||||
|
|
||||||
|
**依存**:
|
||||||
|
- 依存可能: `lib/db/sqlite.ts`, `lib/types/`
|
||||||
|
- 依存禁止: `services/`, `app/`, `lib/sse/`(業務ロジック・配信を持たない)
|
||||||
|
|
||||||
|
### services/ (Service層)
|
||||||
|
|
||||||
|
**役割**: 業務ロジック・権限チェック・トランザクション境界・副作用(通知生成・アクティビティログ記録・SSE配信)。
|
||||||
|
|
||||||
|
**配置ファイル**:
|
||||||
|
```
|
||||||
|
services/
|
||||||
|
├── AuthService.ts
|
||||||
|
├── ProjectService.ts
|
||||||
|
├── ChatService.ts
|
||||||
|
├── MeetingService.ts
|
||||||
|
├── ScheduleService.ts
|
||||||
|
├── FileStorageService.ts
|
||||||
|
├── BackupService.ts
|
||||||
|
├── TodoService.ts
|
||||||
|
├── BoardService.ts
|
||||||
|
├── NoteService.ts
|
||||||
|
├── NotificationService.ts
|
||||||
|
└── ActivityLogService.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: PascalCase + `Service` 接尾
|
||||||
|
|
||||||
|
**依存**:
|
||||||
|
- 依存可能: `repositories/`, `lib/sse/`, `lib/validators/`, `lib/types/`, `lib/db/`(トランザクションのため)
|
||||||
|
- 依存禁止: `app/`(UI層への依存)
|
||||||
|
|
||||||
|
### components/ (Reactコンポーネント)
|
||||||
|
|
||||||
|
**役割**: 再利用可能なUIコンポーネント。機能領域ごとに分割。
|
||||||
|
|
||||||
|
**配置ファイル**: 機能ディレクトリ配下にコンポーネント。
|
||||||
|
```
|
||||||
|
components/
|
||||||
|
├── layout/ # Header, Sidebar, ProjectNav
|
||||||
|
├── project/ # ProjectCard, DashboardWidget
|
||||||
|
├── board/ # ThreadList, ThreadForm, CommentList
|
||||||
|
├── chat/ # ChatWindow, MessageInput, MessageList
|
||||||
|
├── todo/ # KanbanBoard, KanbanColumn, TodoCard
|
||||||
|
├── files/ # FileList, Uploader, Lightbox
|
||||||
|
├── calendar/ # CalendarView, EventBadge
|
||||||
|
├── meetings/ # MeetingForm, ConflictWarning
|
||||||
|
├── notes/ # NoteEditor, MarkdownPreview
|
||||||
|
└── notifications/ # NotificationList, NotificationBadge
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: コンポーネントファイルは PascalCase(`KanbanBoard.tsx`)
|
||||||
|
|
||||||
|
**依存**: `app/` から利用される。`services/` 型・`lib/types/` を参照可能。直接 `repositories/` は触らない。
|
||||||
|
|
||||||
|
### tests/ (テストディレクトリ)
|
||||||
|
|
||||||
|
#### tests/unit/
|
||||||
|
|
||||||
|
**役割**: Unit Test(Vitest)。本番コードと同じ構造をミラーする。
|
||||||
|
|
||||||
|
**構成**:
|
||||||
|
```
|
||||||
|
tests/unit/
|
||||||
|
├── lib/db/sqlite.test.ts
|
||||||
|
├── lib/db/migrator.test.ts
|
||||||
|
├── lib/sse/hub.test.ts
|
||||||
|
├── repositories/UserRepository.test.ts
|
||||||
|
├── repositories/ProjectRepository.test.ts
|
||||||
|
├── ...
|
||||||
|
└── services/ChatService.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: `[対象ファイル].test.ts`(対象と同名 + `.test`)
|
||||||
|
|
||||||
|
#### tests/integration/
|
||||||
|
|
||||||
|
**役割**: 統合テスト。実際のSQLite(一時ファイル)を使用。
|
||||||
|
|
||||||
|
**構成**:
|
||||||
|
```
|
||||||
|
tests/integration/
|
||||||
|
├── auth-flow.test.ts
|
||||||
|
├── project-member-permission.test.ts
|
||||||
|
└── chat-sse-broadcast.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: `[シナリオ].test.ts`(kebab-case)
|
||||||
|
|
||||||
|
#### tests/e2e/
|
||||||
|
|
||||||
|
**役割**: E2E Test(Playwright)。ユーザーシナリオごとに分割。
|
||||||
|
|
||||||
|
**構成**:
|
||||||
|
```
|
||||||
|
tests/e2e/
|
||||||
|
├── auth.spec.ts
|
||||||
|
├── project-management.spec.ts
|
||||||
|
├── board.spec.ts
|
||||||
|
├── chat-sse.spec.ts
|
||||||
|
├── todo-kanban.spec.ts
|
||||||
|
├── file-sharing.spec.ts
|
||||||
|
├── markdown-notes.spec.ts
|
||||||
|
├── calendar.spec.ts
|
||||||
|
├── meetings.spec.ts
|
||||||
|
├── notifications.spec.ts
|
||||||
|
├── activity-log.spec.ts
|
||||||
|
└── backup.spec.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: `[領域].spec.ts`
|
||||||
|
|
||||||
|
### data/ ・ backups/ (永続化データ)
|
||||||
|
|
||||||
|
**役割**: SQLite DBファイル・アップロードファイル・バックアップZIPの永続化先。git管理外。
|
||||||
|
|
||||||
|
```
|
||||||
|
data/
|
||||||
|
├── app.db # SQLite DBファイル
|
||||||
|
└── uploads/<projectId>/<uuid>.<ext>
|
||||||
|
backups/
|
||||||
|
└── backup-<timestamp>.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### docs/ (ドキュメント)
|
||||||
|
|
||||||
|
**配置ファイル**:
|
||||||
|
- `product-requirements.md`: PRD
|
||||||
|
- `functional-design.md`: 機能設計書
|
||||||
|
- `architecture.md`: アーキテクチャ設計書
|
||||||
|
- `repository-structure.md`: 本書
|
||||||
|
- `development-guidelines.md`: 開発ガイドライン
|
||||||
|
- `glossary.md`: 用語集
|
||||||
|
- `ideas/`: ブレインストーミング成果物
|
||||||
|
|
||||||
|
### .steering/ (ステアリングファイル)
|
||||||
|
|
||||||
|
**役割**: ワーク単位の要件・設計・タスクリスト。
|
||||||
|
|
||||||
|
**構成**:
|
||||||
|
```
|
||||||
|
.steering/[YYYYMMDD]-[task-name]/
|
||||||
|
├── requirements.md
|
||||||
|
├── design.md
|
||||||
|
└── tasklist.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名規則**: `20250115-add-user-profile` 形式
|
||||||
|
|
||||||
|
### .opencode/ (opencode設定)
|
||||||
|
|
||||||
|
**役割**: opencode設定・カスタマイズ。
|
||||||
|
|
||||||
|
**構成**: `command/`, `skills/`, `agent/`
|
||||||
|
|
||||||
|
## ファイル配置ルール
|
||||||
|
|
||||||
|
### ソースファイル
|
||||||
|
|
||||||
|
| ファイル種別 | 配置先 | 命名規則 | 例 |
|
||||||
|
|------------|--------|---------|-----|
|
||||||
|
| Route Handler | `app/api/.../route.ts` | `route.ts`(固定) | `app/api/projects/route.ts` |
|
||||||
|
| 画面 | `app/.../page.tsx` | `page.tsx`(固定) | `app/dashboard/page.tsx` |
|
||||||
|
| Repository | `repositories/` | PascalCase + `Repository` | `UserRepository.ts` |
|
||||||
|
| Service | `services/` | PascalCase + `Service` | `ChatService.ts` |
|
||||||
|
| SQLラッパ | `lib/db/` | camelCase | `sqlite.ts` |
|
||||||
|
| Migration | `lib/db/migrations/` | `NNN_description.sql` | `001_initial.sql` |
|
||||||
|
| Entity型 | `lib/types/` | PascalCase | `TodoItem.ts` |
|
||||||
|
| バリデータ | `lib/validators/` | camelCase + `Validator` | `todoValidator.ts` |
|
||||||
|
| Reactコンポーネント | `components/<area>/` | PascalCase | `KanbanBoard.tsx` |
|
||||||
|
| ユーティリティ関数 | `lib/` 配下の適切な領域 | camelCase + 動詞始まり | `formatDate.ts` |
|
||||||
|
|
||||||
|
### テストファイル
|
||||||
|
|
||||||
|
| テスト種別 | 配置先 | 命名規則 | 例 |
|
||||||
|
|-----------|--------|---------|-----|
|
||||||
|
| Unit test | `tests/unit/` | `[対象].test.ts` | `ChatService.test.ts` |
|
||||||
|
| Integration test | `tests/integration/` | `[シナリオ].test.ts` | `chat-sse-broadcast.test.ts` |
|
||||||
|
| E2E test | `tests/e2e/` | `[領域].spec.ts` | `todo-kanban.spec.ts` |
|
||||||
|
|
||||||
|
### 設定ファイル
|
||||||
|
|
||||||
|
| ファイル種別 | 配置先 | 命名規則 |
|
||||||
|
|------------|--------|---------|
|
||||||
|
| ツール設定 | プロジェクトルート | `[tool].config.{js,ts,mjs}`(`next.config.mjs`, `vitest.config.ts`, `playwright.config.ts`) |
|
||||||
|
| 環境変数 | プロジェクトルート | `.env`, `.env.example` |
|
||||||
|
| TypeScript設定 | プロジェクトルート | `tsconfig.json` |
|
||||||
|
|
||||||
|
## 命名規則
|
||||||
|
|
||||||
|
### ディレクトリ名
|
||||||
|
- **レイヤディレクトリ**: 複数形・kebab-case(`repositories/`, `services/`, `components/`)
|
||||||
|
- **機能ディレクトリ**: 単数形・kebab-case(`board/`, `chat/`, `meetings/`)※画面はNext.js規約に従う
|
||||||
|
- **汎用ディレクトリ名禁止**: `utils/`, `misc/`, `common/` は役割が曖昧になるため避ける
|
||||||
|
|
||||||
|
### ファイル名
|
||||||
|
- **クラスファイル**: PascalCase + 役割接尾(`UserRepository.ts`, `ChatService.ts`)
|
||||||
|
- **関数ファイル**: camelCase + 動詞始まり(`formatDate.ts`, `validateEmail.ts`)
|
||||||
|
- **型定義**: PascalCase(`TodoItem.ts`)
|
||||||
|
- **Route Handler/画面**: Next.js固定名(`route.ts`, `page.tsx`, `layout.tsx`)
|
||||||
|
|
||||||
|
## 依存規則
|
||||||
|
|
||||||
|
### レイヤ間の依存
|
||||||
|
|
||||||
|
```
|
||||||
|
app/ (UI層)
|
||||||
|
↓ (OK)
|
||||||
|
services/ (Service層)
|
||||||
|
↓ (OK)
|
||||||
|
repositories/ (Repository層)
|
||||||
|
↓ (OK)
|
||||||
|
lib/db/ (Data層)
|
||||||
|
```
|
||||||
|
|
||||||
|
**禁止依存**:
|
||||||
|
- `lib/db/` → `repositories/`/`services/`/`app/`(下位から上位へ ❌)
|
||||||
|
- `repositories/` → `services/`/`app/`(❌)
|
||||||
|
- `services/` → `app/`(❌)
|
||||||
|
- `app/` → `repositories/`/`lib/db/` の直接アクセス(層飛ばし ❌、Service経由のみ)
|
||||||
|
- `repositories/` → better-sqlite3 直接(❌、`lib/db/sqlite.ts` 経由のみ)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 許容: UI → Service → Repository → Data
|
||||||
|
import { projectService } from '@/services/ProjectService';
|
||||||
|
// ProjectService内:
|
||||||
|
import { projectRepository } from '@/repositories/ProjectRepository';
|
||||||
|
// ProjectRepository内:
|
||||||
|
import { getDb } from '@/lib/db/sqlite';
|
||||||
|
|
||||||
|
// ❌ 禁止: Route HandlerがRepositoryを直接呼ぶ
|
||||||
|
import { projectRepository } from '@/repositories/ProjectRepository'; // app/api/... から ❌
|
||||||
|
```
|
||||||
|
|
||||||
|
### モジュール間の依存
|
||||||
|
|
||||||
|
**循環依存禁止**: Service間で循環依存が生じる場合は、共有型を `lib/types/` に抽出するか、共通機能を別Service(例: `NotificationService`, `ActivityLogService`)に切り出す。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ 循環依存
|
||||||
|
// services/TaskService.ts
|
||||||
|
import { UserService } from './UserService';
|
||||||
|
// services/UserService.ts
|
||||||
|
import { TaskService } from './TaskService';
|
||||||
|
|
||||||
|
// ✅ 解決: 共通副作用を別Serviceに切り出し
|
||||||
|
// services/NotificationService.ts ← 両方から利用
|
||||||
|
// services/ActivityLogService.ts ← 両方から利用
|
||||||
|
```
|
||||||
|
|
||||||
|
### パスエイリアス
|
||||||
|
|
||||||
|
`tsconfig.json` で `@/*` をプロジェクトルートにマッピングし、相対パスの深い `../../` を避ける。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "compilerOptions": { "paths": { "@/*": ["./*"] } } }
|
||||||
|
```
|
||||||
|
|
||||||
|
## スケーリング戦略
|
||||||
|
|
||||||
|
### 機能追加時の配置方針
|
||||||
|
|
||||||
|
1. **小機能**: 既存ディレクトリに追加(例: 新しいRepository → `repositories/` に1ファイル)
|
||||||
|
2. **中機能**: レイヤ内にサブディレクトリ作成(例: `services/meeting/` 配下に複数Service)
|
||||||
|
3. **大機能**: 独立モジュール化を検討
|
||||||
|
|
||||||
|
### ファイルサイズ管理
|
||||||
|
|
||||||
|
- 1ファイル **300行以下** 推奨
|
||||||
|
- 300-500行: リファクタリング検討
|
||||||
|
- 500行超: 分割推奨(責務ごとに分割。例: `TaskService.ts` → `TaskService.ts` + `TaskValidationService.ts` + `TaskNotificationService.ts`)
|
||||||
|
|
||||||
|
### モジュール分離のタイミング
|
||||||
|
|
||||||
|
以下の兆候があれば機能単位のモジュール化を検討:
|
||||||
|
- 1ディレクトリに10ファイル超
|
||||||
|
- 関連機能がグループ化されている
|
||||||
|
- 独立してテスト可能
|
||||||
|
- 他機能への依存が少ない
|
||||||
|
|
||||||
|
## 除外設定
|
||||||
|
|
||||||
|
### .gitignore
|
||||||
|
|
||||||
|
除外対象:
|
||||||
|
- `node_modules/`
|
||||||
|
- `.next/`
|
||||||
|
- `dist/`
|
||||||
|
- `coverage/`
|
||||||
|
- `data/`(DB・uploads・一時データ)
|
||||||
|
- `backups/`
|
||||||
|
- `.env`
|
||||||
|
- `.steering/`(タスク管理の一時ファイル)
|
||||||
|
- `*.log`
|
||||||
|
- `test-results/`, `playwright-report/`
|
||||||
|
- `.DS_Store`
|
||||||
|
|
||||||
|
### ツール除外
|
||||||
|
|
||||||
|
`.prettierignore`, `.eslintignore`:
|
||||||
|
- `dist/`, `node_modules/`, `.next/`, `coverage/`, `data/`, `backups/`
|
||||||
|
|
||||||
|
## チェックリスト
|
||||||
|
|
||||||
|
- [x] 各ディレクトリの役割が明確に定義されている
|
||||||
|
- [x] レイヤ構造がディレクトリに反映されている
|
||||||
|
- [x] 命名規則が一貫している
|
||||||
|
- [x] テストコードの配置方針が決まっている
|
||||||
|
- [x] 依存規則が明確である
|
||||||
|
- [x] 循環依存を避ける方針がある
|
||||||
|
- [x] スケーリング戦略が考慮されている
|
||||||
|
- [x] 共有コードの配置ルールが定義されている(`lib/types/`, `lib/validators/`)
|
||||||
|
- [x] 設定ファイルの管理方法が決まっている
|
||||||
|
- [x] ドキュメントの配置が明確である
|
||||||
23
eslint.config.js
Normal file
23
eslint.config.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import prettierConfig from 'eslint-config-prettier';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
prettierConfig,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{ argsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['node_modules/**', 'dist/**', '.steering/**'],
|
||||||
|
}
|
||||||
|
);
|
||||||
7
opencode.json
Normal file
7
opencode.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"instructions": [
|
||||||
|
"AGENTS.md"
|
||||||
|
],
|
||||||
|
"permission": "allow"
|
||||||
|
}
|
||||||
44
package.json
Normal file
44
package.json
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "opencode-spec-driven-boilerplate",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Spec-driven development boilerplate for opencode",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "husky",
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "npx prettier --write .",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"test:ui": "vitest --ui"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"spec-driven-development",
|
||||||
|
"opencode",
|
||||||
|
"template"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.11.0",
|
||||||
|
"@vitest/coverage-v8": "^2.0.0",
|
||||||
|
"@vitest/ui": "^2.0.0",
|
||||||
|
"eslint": "^9.0.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"husky": "^9.0.0",
|
||||||
|
"lint-staged": "^15.2.0",
|
||||||
|
"prettier": "^3.2.0",
|
||||||
|
"typescript": "~5.3.0",
|
||||||
|
"typescript-eslint": "^8.0.0",
|
||||||
|
"vitest": "^2.0.0"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1
prompt.md
Normal file
1
prompt.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
/add-feature Please develop the core feature (MVP) defined in the product requirements document.
|
||||||
93
src/example.test.ts
Normal file
93
src/example.test.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { add, subtract, multiply, divide } from './example';
|
||||||
|
|
||||||
|
describe('Math operations', () => {
|
||||||
|
describe('add', () => {
|
||||||
|
it('正の数を足すと正しい結果が返る', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = 2;
|
||||||
|
const b = 3;
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = add(a, b);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('負の数を足すと正しい結果が返る', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = -2;
|
||||||
|
const b = 3;
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = add(a, b);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subtract', () => {
|
||||||
|
it('正の数を引くと正しい結果が返る', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = 5;
|
||||||
|
const b = 3;
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = subtract(a, b);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiply', () => {
|
||||||
|
it('正の数をかけると正しい結果が返る', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = 2;
|
||||||
|
const b = 3;
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = multiply(a, b);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result).toBe(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('0をかけると0が返る', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = 5;
|
||||||
|
const b = 0;
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = multiply(a, b);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('divide', () => {
|
||||||
|
it('正の数で割ると正しい結果が返る', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = 6;
|
||||||
|
const b = 3;
|
||||||
|
|
||||||
|
// When: 実行
|
||||||
|
const result = divide(a, b);
|
||||||
|
|
||||||
|
// Then: 検証
|
||||||
|
expect(result).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('0で割るとエラーをスローする', () => {
|
||||||
|
// Given: 準備
|
||||||
|
const a = 5;
|
||||||
|
const b = 0;
|
||||||
|
|
||||||
|
// When/Then: 実行と検証
|
||||||
|
expect(() => divide(a, b)).toThrow('Division by zero is not allowed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
18
src/example.ts
Normal file
18
src/example.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export function add(a: number, b: number): number {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function subtract(a: number, b: number): number {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function multiply(a: number, b: number): number {
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function divide(a: number, b: number): number {
|
||||||
|
if (b === 0) {
|
||||||
|
throw new Error('Division by zero is not allowed');
|
||||||
|
}
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"types": ["vitest/globals"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
29
vitest.config.ts
Normal file
29
vitest.config.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
include: [
|
||||||
|
'src/**/*.{test,spec}.{ts,tsx}',
|
||||||
|
'tests/**/*.{test,spec}.{ts,tsx}',
|
||||||
|
],
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
reporter: ['text', 'json', 'html'],
|
||||||
|
exclude: [
|
||||||
|
'node_modules/**',
|
||||||
|
'dist/**',
|
||||||
|
'.steering/**',
|
||||||
|
'**/*.config.{ts,js}',
|
||||||
|
'**/types/**',
|
||||||
|
],
|
||||||
|
thresholds: {
|
||||||
|
branches: 80,
|
||||||
|
functions: 80,
|
||||||
|
lines: 80,
|
||||||
|
statements: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user