devflow-kit 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +185 -29
- package/LICENSE +1 -1
- package/README.md +179 -308
- package/dist/cli.js +3 -1
- package/dist/commands/init.d.ts +21 -0
- package/dist/commands/init.js +311 -575
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.js +20 -0
- package/dist/commands/uninstall.d.ts +10 -0
- package/dist/commands/uninstall.js +351 -78
- package/dist/plugins.d.ts +46 -0
- package/dist/plugins.js +162 -0
- package/dist/utils/cli.d.ts +5 -0
- package/dist/utils/cli.js +14 -0
- package/dist/utils/installer.d.ts +41 -0
- package/dist/utils/installer.js +177 -0
- package/dist/utils/paths.d.ts +10 -0
- package/dist/utils/paths.js +23 -3
- package/dist/utils/post-install.d.ts +68 -0
- package/dist/utils/post-install.js +427 -0
- package/dist/utils/safe-delete-install.d.ts +22 -0
- package/dist/utils/safe-delete-install.js +156 -0
- package/dist/utils/safe-delete.d.ts +12 -0
- package/dist/utils/safe-delete.js +83 -0
- package/package.json +18 -8
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +7 -0
- package/plugins/devflow-audit-claude/README.md +46 -0
- package/plugins/devflow-audit-claude/agents/claude-md-auditor.md +134 -0
- package/plugins/devflow-audit-claude/commands/audit-claude.md +85 -0
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +31 -0
- package/plugins/devflow-code-review/README.md +73 -0
- package/plugins/devflow-code-review/agents/git.md +272 -0
- package/plugins/devflow-code-review/agents/reviewer.md +119 -0
- package/plugins/devflow-code-review/agents/synthesizer.md +204 -0
- package/plugins/devflow-code-review/commands/code-review-teams.md +262 -0
- package/plugins/devflow-code-review/commands/code-review.md +141 -0
- package/plugins/devflow-code-review/skills/accessibility/SKILL.md +229 -0
- package/plugins/devflow-code-review/skills/accessibility/references/detection.md +171 -0
- package/plugins/devflow-code-review/skills/accessibility/references/patterns.md +670 -0
- package/plugins/devflow-code-review/skills/accessibility/references/violations.md +419 -0
- package/plugins/devflow-code-review/skills/agent-teams/SKILL.md +124 -0
- package/plugins/devflow-code-review/skills/agent-teams/references/cleanup.md +104 -0
- package/plugins/devflow-code-review/skills/agent-teams/references/communication.md +122 -0
- package/plugins/devflow-code-review/skills/agent-teams/references/team-patterns.md +217 -0
- package/plugins/devflow-code-review/skills/architecture-patterns/SKILL.md +153 -0
- package/plugins/devflow-code-review/skills/architecture-patterns/references/detection.md +337 -0
- package/plugins/devflow-code-review/skills/architecture-patterns/references/patterns.md +873 -0
- package/plugins/devflow-code-review/skills/architecture-patterns/references/violations.md +575 -0
- package/plugins/devflow-code-review/skills/complexity-patterns/SKILL.md +143 -0
- package/plugins/devflow-code-review/skills/complexity-patterns/references/detection.md +264 -0
- package/plugins/devflow-code-review/skills/complexity-patterns/references/patterns.md +487 -0
- package/plugins/devflow-code-review/skills/complexity-patterns/references/violations.md +361 -0
- package/plugins/devflow-code-review/skills/consistency-patterns/SKILL.md +140 -0
- package/plugins/devflow-code-review/skills/consistency-patterns/references/detection.md +207 -0
- package/plugins/devflow-code-review/skills/consistency-patterns/references/patterns.md +202 -0
- package/plugins/devflow-code-review/skills/consistency-patterns/references/violations.md +213 -0
- package/plugins/devflow-code-review/skills/database-patterns/SKILL.md +134 -0
- package/plugins/devflow-code-review/skills/database-patterns/references/detection.md +208 -0
- package/plugins/devflow-code-review/skills/database-patterns/references/patterns.md +394 -0
- package/plugins/devflow-code-review/skills/database-patterns/references/violations.md +332 -0
- package/plugins/devflow-code-review/skills/dependencies-patterns/SKILL.md +141 -0
- package/plugins/devflow-code-review/skills/dependencies-patterns/references/detection.md +181 -0
- package/plugins/devflow-code-review/skills/dependencies-patterns/references/patterns.md +225 -0
- package/plugins/devflow-code-review/skills/dependencies-patterns/references/violations.md +247 -0
- package/plugins/devflow-code-review/skills/documentation-patterns/SKILL.md +125 -0
- package/plugins/devflow-code-review/skills/documentation-patterns/references/detection.md +190 -0
- package/plugins/devflow-code-review/skills/documentation-patterns/references/patterns.md +189 -0
- package/plugins/devflow-code-review/skills/documentation-patterns/references/violations.md +163 -0
- package/plugins/devflow-code-review/skills/frontend-design/SKILL.md +254 -0
- package/plugins/devflow-code-review/skills/frontend-design/references/detection.md +184 -0
- package/plugins/devflow-code-review/skills/frontend-design/references/patterns.md +511 -0
- package/plugins/devflow-code-review/skills/frontend-design/references/violations.md +453 -0
- package/plugins/devflow-code-review/skills/performance-patterns/SKILL.md +154 -0
- package/plugins/devflow-code-review/skills/performance-patterns/references/detection.md +351 -0
- package/plugins/devflow-code-review/skills/performance-patterns/references/patterns.md +503 -0
- package/plugins/devflow-code-review/skills/performance-patterns/references/violations.md +354 -0
- package/plugins/devflow-code-review/skills/react/SKILL.md +276 -0
- package/plugins/devflow-code-review/skills/react/references/patterns.md +1331 -0
- package/plugins/devflow-code-review/skills/react/references/violations.md +565 -0
- package/plugins/devflow-code-review/skills/regression-patterns/SKILL.md +146 -0
- package/plugins/devflow-code-review/skills/regression-patterns/references/detection.md +237 -0
- package/plugins/devflow-code-review/skills/regression-patterns/references/patterns.md +226 -0
- package/plugins/devflow-code-review/skills/regression-patterns/references/violations.md +225 -0
- package/plugins/devflow-code-review/skills/review-methodology/SKILL.md +119 -0
- package/plugins/devflow-code-review/skills/review-methodology/references/patterns.md +186 -0
- package/plugins/devflow-code-review/skills/review-methodology/references/report-template.md +142 -0
- package/plugins/devflow-code-review/skills/review-methodology/references/violations.md +125 -0
- package/plugins/devflow-code-review/skills/security-patterns/SKILL.md +156 -0
- package/plugins/devflow-code-review/skills/security-patterns/references/detection.md +287 -0
- package/plugins/devflow-code-review/skills/security-patterns/references/patterns.md +507 -0
- package/plugins/devflow-code-review/skills/security-patterns/references/violations.md +237 -0
- package/plugins/devflow-code-review/skills/test-patterns/SKILL.md +183 -0
- package/plugins/devflow-code-review/skills/test-patterns/references/detection.md +149 -0
- package/plugins/devflow-code-review/skills/test-patterns/references/patterns.md +220 -0
- package/plugins/devflow-code-review/skills/test-patterns/references/report-template.md +108 -0
- package/plugins/devflow-code-review/skills/test-patterns/references/violations.md +221 -0
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +27 -0
- package/plugins/devflow-core-skills/README.md +50 -0
- package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +229 -0
- package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +171 -0
- package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +670 -0
- package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +419 -0
- package/plugins/devflow-core-skills/skills/core-patterns/SKILL.md +162 -0
- package/plugins/devflow-core-skills/skills/core-patterns/references/checklist.md +276 -0
- package/plugins/devflow-core-skills/skills/core-patterns/references/code-smell-violations.md +144 -0
- package/plugins/devflow-core-skills/skills/core-patterns/references/detection.md +303 -0
- package/plugins/devflow-core-skills/skills/core-patterns/references/patterns.md +576 -0
- package/plugins/devflow-core-skills/skills/core-patterns/references/violations.md +369 -0
- package/plugins/devflow-core-skills/skills/docs-framework/SKILL.md +134 -0
- package/plugins/devflow-core-skills/skills/docs-framework/references/patterns.md +346 -0
- package/plugins/devflow-core-skills/skills/docs-framework/references/violations.md +221 -0
- package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +254 -0
- package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +184 -0
- package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +511 -0
- package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +453 -0
- package/plugins/devflow-core-skills/skills/git-safety/SKILL.md +122 -0
- package/plugins/devflow-core-skills/skills/git-safety/references/detection.md +290 -0
- package/plugins/devflow-core-skills/skills/git-safety/references/patterns.md +289 -0
- package/plugins/devflow-core-skills/skills/git-safety/references/violations.md +18 -0
- package/plugins/devflow-core-skills/skills/git-workflow/SKILL.md +158 -0
- package/plugins/devflow-core-skills/skills/git-workflow/references/commit-patterns.md +115 -0
- package/plugins/devflow-core-skills/skills/git-workflow/references/commit-violations.md +77 -0
- package/plugins/devflow-core-skills/skills/git-workflow/references/pr-patterns.md +127 -0
- package/plugins/devflow-core-skills/skills/git-workflow/references/pr-violations.md +96 -0
- package/plugins/devflow-core-skills/skills/github-patterns/SKILL.md +153 -0
- package/plugins/devflow-core-skills/skills/github-patterns/references/patterns.md +572 -0
- package/plugins/devflow-core-skills/skills/github-patterns/references/violations.md +298 -0
- package/plugins/devflow-core-skills/skills/input-validation/SKILL.md +148 -0
- package/plugins/devflow-core-skills/skills/input-validation/references/detection.md +283 -0
- package/plugins/devflow-core-skills/skills/input-validation/references/patterns.md +361 -0
- package/plugins/devflow-core-skills/skills/input-validation/references/violations.md +224 -0
- package/plugins/devflow-core-skills/skills/react/SKILL.md +276 -0
- package/plugins/devflow-core-skills/skills/react/references/patterns.md +1331 -0
- package/plugins/devflow-core-skills/skills/react/references/violations.md +565 -0
- package/plugins/devflow-core-skills/skills/test-patterns/SKILL.md +183 -0
- package/plugins/devflow-core-skills/skills/test-patterns/references/detection.md +149 -0
- package/plugins/devflow-core-skills/skills/test-patterns/references/patterns.md +220 -0
- package/plugins/devflow-core-skills/skills/test-patterns/references/report-template.md +108 -0
- package/plugins/devflow-core-skills/skills/test-patterns/references/violations.md +221 -0
- package/plugins/devflow-core-skills/skills/typescript/SKILL.md +176 -0
- package/plugins/devflow-core-skills/skills/typescript/references/patterns.md +1105 -0
- package/plugins/devflow-core-skills/skills/typescript/references/violations.md +433 -0
- package/plugins/devflow-debug/.claude-plugin/plugin.json +18 -0
- package/plugins/devflow-debug/README.md +65 -0
- package/plugins/devflow-debug/agents/git.md +272 -0
- package/plugins/devflow-debug/commands/debug-teams.md +231 -0
- package/plugins/devflow-debug/commands/debug.md +160 -0
- package/plugins/devflow-debug/skills/agent-teams/SKILL.md +124 -0
- package/plugins/devflow-debug/skills/agent-teams/references/cleanup.md +104 -0
- package/plugins/devflow-debug/skills/agent-teams/references/communication.md +122 -0
- package/plugins/devflow-debug/skills/agent-teams/references/team-patterns.md +217 -0
- package/plugins/devflow-debug/skills/git-safety/SKILL.md +122 -0
- package/plugins/devflow-debug/skills/git-safety/references/detection.md +290 -0
- package/plugins/devflow-debug/skills/git-safety/references/patterns.md +289 -0
- package/plugins/devflow-debug/skills/git-safety/references/violations.md +18 -0
- package/plugins/devflow-implement/.claude-plugin/plugin.json +21 -0
- package/plugins/devflow-implement/README.md +71 -0
- package/plugins/devflow-implement/agents/coder.md +122 -0
- package/plugins/devflow-implement/agents/git.md +272 -0
- package/plugins/devflow-implement/agents/scrutinizer.md +80 -0
- package/plugins/devflow-implement/agents/shepherd.md +94 -0
- package/plugins/devflow-implement/agents/simplifier.md +62 -0
- package/plugins/devflow-implement/agents/skimmer.md +88 -0
- package/plugins/devflow-implement/agents/synthesizer.md +204 -0
- package/plugins/devflow-implement/agents/validator.md +86 -0
- package/plugins/devflow-implement/commands/implement-teams.md +608 -0
- package/plugins/devflow-implement/commands/implement.md +426 -0
- package/plugins/devflow-implement/skills/accessibility/SKILL.md +229 -0
- package/plugins/devflow-implement/skills/accessibility/references/detection.md +171 -0
- package/plugins/devflow-implement/skills/accessibility/references/patterns.md +670 -0
- package/plugins/devflow-implement/skills/accessibility/references/violations.md +419 -0
- package/plugins/devflow-implement/skills/agent-teams/SKILL.md +124 -0
- package/plugins/devflow-implement/skills/agent-teams/references/cleanup.md +104 -0
- package/plugins/devflow-implement/skills/agent-teams/references/communication.md +122 -0
- package/plugins/devflow-implement/skills/agent-teams/references/team-patterns.md +217 -0
- package/plugins/devflow-implement/skills/frontend-design/SKILL.md +254 -0
- package/plugins/devflow-implement/skills/frontend-design/references/detection.md +184 -0
- package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +511 -0
- package/plugins/devflow-implement/skills/frontend-design/references/violations.md +453 -0
- package/plugins/devflow-implement/skills/implementation-patterns/SKILL.md +162 -0
- package/plugins/devflow-implement/skills/implementation-patterns/references/patterns.md +1063 -0
- package/plugins/devflow-implement/skills/implementation-patterns/references/violations.md +483 -0
- package/plugins/devflow-implement/skills/self-review/SKILL.md +149 -0
- package/plugins/devflow-implement/skills/self-review/references/patterns.md +405 -0
- package/plugins/devflow-implement/skills/self-review/references/report-template.md +253 -0
- package/plugins/devflow-implement/skills/self-review/references/violations.md +308 -0
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +19 -0
- package/plugins/devflow-resolve/README.md +65 -0
- package/plugins/devflow-resolve/agents/git.md +272 -0
- package/plugins/devflow-resolve/agents/resolver.md +131 -0
- package/plugins/devflow-resolve/agents/simplifier.md +62 -0
- package/plugins/devflow-resolve/commands/resolve-teams.md +298 -0
- package/plugins/devflow-resolve/commands/resolve.md +237 -0
- package/plugins/devflow-resolve/skills/agent-teams/SKILL.md +124 -0
- package/plugins/devflow-resolve/skills/agent-teams/references/cleanup.md +104 -0
- package/plugins/devflow-resolve/skills/agent-teams/references/communication.md +122 -0
- package/plugins/devflow-resolve/skills/agent-teams/references/team-patterns.md +217 -0
- package/plugins/devflow-resolve/skills/implementation-patterns/SKILL.md +162 -0
- package/plugins/devflow-resolve/skills/implementation-patterns/references/patterns.md +1063 -0
- package/plugins/devflow-resolve/skills/implementation-patterns/references/violations.md +483 -0
- package/plugins/devflow-resolve/skills/security-patterns/SKILL.md +156 -0
- package/plugins/devflow-resolve/skills/security-patterns/references/detection.md +287 -0
- package/plugins/devflow-resolve/skills/security-patterns/references/patterns.md +507 -0
- package/plugins/devflow-resolve/skills/security-patterns/references/violations.md +237 -0
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +7 -0
- package/plugins/devflow-self-review/README.md +38 -0
- package/plugins/devflow-self-review/agents/scrutinizer.md +80 -0
- package/plugins/devflow-self-review/agents/simplifier.md +62 -0
- package/plugins/devflow-self-review/agents/validator.md +86 -0
- package/plugins/devflow-self-review/commands/self-review.md +126 -0
- package/plugins/devflow-self-review/skills/core-patterns/SKILL.md +162 -0
- package/plugins/devflow-self-review/skills/core-patterns/references/checklist.md +276 -0
- package/plugins/devflow-self-review/skills/core-patterns/references/code-smell-violations.md +144 -0
- package/plugins/devflow-self-review/skills/core-patterns/references/detection.md +303 -0
- package/plugins/devflow-self-review/skills/core-patterns/references/patterns.md +576 -0
- package/plugins/devflow-self-review/skills/core-patterns/references/violations.md +369 -0
- package/plugins/devflow-self-review/skills/self-review/SKILL.md +149 -0
- package/plugins/devflow-self-review/skills/self-review/references/patterns.md +405 -0
- package/plugins/devflow-self-review/skills/self-review/references/report-template.md +253 -0
- package/plugins/devflow-self-review/skills/self-review/references/violations.md +308 -0
- package/plugins/devflow-specify/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-specify/README.md +46 -0
- package/plugins/devflow-specify/agents/skimmer.md +88 -0
- package/plugins/devflow-specify/agents/synthesizer.md +204 -0
- package/plugins/devflow-specify/commands/specify-teams.md +314 -0
- package/plugins/devflow-specify/commands/specify.md +179 -0
- package/plugins/devflow-specify/skills/agent-teams/SKILL.md +124 -0
- package/plugins/devflow-specify/skills/agent-teams/references/cleanup.md +104 -0
- package/plugins/devflow-specify/skills/agent-teams/references/communication.md +122 -0
- package/plugins/devflow-specify/skills/agent-teams/references/team-patterns.md +217 -0
- package/scripts/hooks/background-memory-update.sh +167 -0
- package/scripts/hooks/pre-compact-memory.sh +81 -0
- package/scripts/hooks/session-start-memory.sh +84 -0
- package/scripts/hooks/stop-update-memory.sh +81 -0
- package/shared/agents/coder.md +122 -0
- package/shared/agents/git.md +272 -0
- package/shared/agents/resolver.md +131 -0
- package/shared/agents/reviewer.md +119 -0
- package/shared/agents/scrutinizer.md +80 -0
- package/shared/agents/shepherd.md +94 -0
- package/shared/agents/simplifier.md +62 -0
- package/shared/agents/skimmer.md +88 -0
- package/shared/agents/synthesizer.md +204 -0
- package/shared/agents/validator.md +86 -0
- package/shared/skills/accessibility/SKILL.md +229 -0
- package/shared/skills/accessibility/references/detection.md +171 -0
- package/shared/skills/accessibility/references/patterns.md +670 -0
- package/shared/skills/accessibility/references/violations.md +419 -0
- package/shared/skills/agent-teams/SKILL.md +124 -0
- package/shared/skills/agent-teams/references/cleanup.md +104 -0
- package/shared/skills/agent-teams/references/communication.md +122 -0
- package/shared/skills/agent-teams/references/team-patterns.md +217 -0
- package/shared/skills/architecture-patterns/SKILL.md +153 -0
- package/shared/skills/architecture-patterns/references/detection.md +337 -0
- package/shared/skills/architecture-patterns/references/patterns.md +873 -0
- package/shared/skills/architecture-patterns/references/violations.md +575 -0
- package/shared/skills/complexity-patterns/SKILL.md +143 -0
- package/shared/skills/complexity-patterns/references/detection.md +264 -0
- package/shared/skills/complexity-patterns/references/patterns.md +487 -0
- package/shared/skills/complexity-patterns/references/violations.md +361 -0
- package/shared/skills/consistency-patterns/SKILL.md +140 -0
- package/shared/skills/consistency-patterns/references/detection.md +207 -0
- package/shared/skills/consistency-patterns/references/patterns.md +202 -0
- package/shared/skills/consistency-patterns/references/violations.md +213 -0
- package/shared/skills/core-patterns/SKILL.md +162 -0
- package/shared/skills/core-patterns/references/checklist.md +276 -0
- package/shared/skills/core-patterns/references/code-smell-violations.md +144 -0
- package/shared/skills/core-patterns/references/detection.md +303 -0
- package/shared/skills/core-patterns/references/patterns.md +576 -0
- package/shared/skills/core-patterns/references/violations.md +369 -0
- package/shared/skills/database-patterns/SKILL.md +134 -0
- package/shared/skills/database-patterns/references/detection.md +208 -0
- package/shared/skills/database-patterns/references/patterns.md +394 -0
- package/shared/skills/database-patterns/references/violations.md +332 -0
- package/shared/skills/dependencies-patterns/SKILL.md +141 -0
- package/shared/skills/dependencies-patterns/references/detection.md +181 -0
- package/shared/skills/dependencies-patterns/references/patterns.md +225 -0
- package/shared/skills/dependencies-patterns/references/violations.md +247 -0
- package/shared/skills/docs-framework/SKILL.md +134 -0
- package/shared/skills/docs-framework/references/patterns.md +346 -0
- package/shared/skills/docs-framework/references/violations.md +221 -0
- package/shared/skills/documentation-patterns/SKILL.md +125 -0
- package/shared/skills/documentation-patterns/references/detection.md +190 -0
- package/shared/skills/documentation-patterns/references/patterns.md +189 -0
- package/shared/skills/documentation-patterns/references/violations.md +163 -0
- package/shared/skills/frontend-design/SKILL.md +254 -0
- package/shared/skills/frontend-design/references/detection.md +184 -0
- package/shared/skills/frontend-design/references/patterns.md +511 -0
- package/shared/skills/frontend-design/references/violations.md +453 -0
- package/shared/skills/git-safety/SKILL.md +122 -0
- package/shared/skills/git-safety/references/detection.md +290 -0
- package/shared/skills/git-safety/references/patterns.md +289 -0
- package/shared/skills/git-safety/references/violations.md +18 -0
- package/shared/skills/git-workflow/SKILL.md +158 -0
- package/shared/skills/git-workflow/references/commit-patterns.md +115 -0
- package/shared/skills/git-workflow/references/commit-violations.md +77 -0
- package/shared/skills/git-workflow/references/pr-patterns.md +127 -0
- package/shared/skills/git-workflow/references/pr-violations.md +96 -0
- package/shared/skills/github-patterns/SKILL.md +153 -0
- package/shared/skills/github-patterns/references/patterns.md +572 -0
- package/shared/skills/github-patterns/references/violations.md +298 -0
- package/shared/skills/implementation-patterns/SKILL.md +162 -0
- package/shared/skills/implementation-patterns/references/patterns.md +1063 -0
- package/shared/skills/implementation-patterns/references/violations.md +483 -0
- package/shared/skills/input-validation/SKILL.md +148 -0
- package/shared/skills/input-validation/references/detection.md +283 -0
- package/shared/skills/input-validation/references/patterns.md +361 -0
- package/shared/skills/input-validation/references/violations.md +224 -0
- package/shared/skills/performance-patterns/SKILL.md +154 -0
- package/shared/skills/performance-patterns/references/detection.md +351 -0
- package/shared/skills/performance-patterns/references/patterns.md +503 -0
- package/shared/skills/performance-patterns/references/violations.md +354 -0
- package/shared/skills/react/SKILL.md +276 -0
- package/shared/skills/react/references/patterns.md +1331 -0
- package/shared/skills/react/references/violations.md +565 -0
- package/shared/skills/regression-patterns/SKILL.md +146 -0
- package/shared/skills/regression-patterns/references/detection.md +237 -0
- package/shared/skills/regression-patterns/references/patterns.md +226 -0
- package/shared/skills/regression-patterns/references/violations.md +225 -0
- package/shared/skills/review-methodology/SKILL.md +119 -0
- package/shared/skills/review-methodology/references/patterns.md +186 -0
- package/shared/skills/review-methodology/references/report-template.md +142 -0
- package/shared/skills/review-methodology/references/violations.md +125 -0
- package/shared/skills/security-patterns/SKILL.md +156 -0
- package/shared/skills/security-patterns/references/detection.md +287 -0
- package/shared/skills/security-patterns/references/patterns.md +507 -0
- package/shared/skills/security-patterns/references/violations.md +237 -0
- package/shared/skills/self-review/SKILL.md +149 -0
- package/shared/skills/self-review/references/patterns.md +405 -0
- package/shared/skills/self-review/references/report-template.md +253 -0
- package/shared/skills/self-review/references/violations.md +308 -0
- package/shared/skills/test-patterns/SKILL.md +183 -0
- package/shared/skills/test-patterns/references/detection.md +149 -0
- package/shared/skills/test-patterns/references/patterns.md +220 -0
- package/shared/skills/test-patterns/references/report-template.md +108 -0
- package/shared/skills/test-patterns/references/violations.md +221 -0
- package/shared/skills/typescript/SKILL.md +176 -0
- package/shared/skills/typescript/references/patterns.md +1105 -0
- package/shared/skills/typescript/references/violations.md +433 -0
- package/src/templates/claudeignore.template +188 -0
- package/src/templates/managed-settings.json +146 -0
- package/src/templates/settings.json +59 -0
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/uninstall.d.ts.map +0 -1
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/paths.d.ts.map +0 -1
- package/dist/utils/paths.js.map +0 -1
- package/src/claude/CLAUDE.md +0 -400
- package/src/claude/agents/devflow/audit-architecture.md +0 -132
- package/src/claude/agents/devflow/audit-complexity.md +0 -132
- package/src/claude/agents/devflow/audit-database.md +0 -132
- package/src/claude/agents/devflow/audit-dependencies.md +0 -132
- package/src/claude/agents/devflow/audit-documentation.md +0 -132
- package/src/claude/agents/devflow/audit-performance.md +0 -256
- package/src/claude/agents/devflow/audit-security.md +0 -259
- package/src/claude/agents/devflow/audit-tests.md +0 -132
- package/src/claude/agents/devflow/audit-typescript.md +0 -132
- package/src/claude/agents/devflow/brainstorm.md +0 -279
- package/src/claude/agents/devflow/catch-up.md +0 -345
- package/src/claude/agents/devflow/code-review.md +0 -307
- package/src/claude/agents/devflow/commit.md +0 -380
- package/src/claude/agents/devflow/debug.md +0 -476
- package/src/claude/agents/devflow/design.md +0 -491
- package/src/claude/agents/devflow/pr-comments.md +0 -285
- package/src/claude/agents/devflow/project-state.md +0 -419
- package/src/claude/agents/devflow/pull-request.md +0 -423
- package/src/claude/agents/devflow/release.md +0 -1137
- package/src/claude/agents/devflow/tech-debt.md +0 -338
- package/src/claude/commands/devflow/brainstorm.md +0 -68
- package/src/claude/commands/devflow/breakdown.md +0 -125
- package/src/claude/commands/devflow/catch-up.md +0 -29
- package/src/claude/commands/devflow/code-review.md +0 -237
- package/src/claude/commands/devflow/commit.md +0 -17
- package/src/claude/commands/devflow/debug.md +0 -56
- package/src/claude/commands/devflow/design.md +0 -82
- package/src/claude/commands/devflow/devlog.md +0 -408
- package/src/claude/commands/devflow/implement.md +0 -100
- package/src/claude/commands/devflow/plan.md +0 -223
- package/src/claude/commands/devflow/pull-request.md +0 -269
- package/src/claude/commands/devflow/release.md +0 -251
- package/src/claude/commands/devflow/resolve-comments.md +0 -583
- package/src/claude/scripts/statusline.sh +0 -47
- package/src/claude/settings.json +0 -6
- package/src/claude/skills/devflow/code-smell/SKILL.md +0 -428
- package/src/claude/skills/devflow/debug/SKILL.md +0 -119
- package/src/claude/skills/devflow/error-handling/SKILL.md +0 -597
- package/src/claude/skills/devflow/input-validation/SKILL.md +0 -514
- package/src/claude/skills/devflow/pattern-check/SKILL.md +0 -238
- package/src/claude/skills/devflow/research/SKILL.md +0 -138
- package/src/claude/skills/devflow/test-design/SKILL.md +0 -384
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
# Architecture Correct Patterns
|
|
2
|
+
|
|
3
|
+
Extended correct patterns for architecture reviews. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## SOLID Patterns
|
|
6
|
+
|
|
7
|
+
### Single Responsibility Principle (SRP)
|
|
8
|
+
|
|
9
|
+
**Proper Separation of Concerns**
|
|
10
|
+
```typescript
|
|
11
|
+
// CORRECT: Each class has one responsibility
|
|
12
|
+
class UserController {
|
|
13
|
+
constructor(
|
|
14
|
+
private userService: UserService,
|
|
15
|
+
private validator: UserValidator
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
async createUser(req, res) {
|
|
19
|
+
const validation = this.validator.validateCreateUser(req.body);
|
|
20
|
+
if (!validation.ok) {
|
|
21
|
+
return res.status(400).json({ error: validation.error });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const result = await this.userService.create(validation.data);
|
|
25
|
+
if (!result.ok) {
|
|
26
|
+
return res.status(result.error.statusCode).json({ error: result.error.message });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
res.json(result.value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class UserService {
|
|
34
|
+
constructor(
|
|
35
|
+
private repository: UserRepository,
|
|
36
|
+
private hasher: PasswordHasher,
|
|
37
|
+
private emailer: EmailService,
|
|
38
|
+
private analytics: AnalyticsService,
|
|
39
|
+
private logger: Logger
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
async create(data: CreateUserData): Promise<Result<UserDTO, ServiceError>> {
|
|
43
|
+
this.logger.info('Creating user', { email: data.email });
|
|
44
|
+
|
|
45
|
+
const existing = await this.repository.findByEmail(data.email);
|
|
46
|
+
if (existing) {
|
|
47
|
+
return Err({ type: 'conflict', message: 'User exists', statusCode: 409 });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const hashedPassword = await this.hasher.hash(data.password);
|
|
51
|
+
const user = await this.repository.create({
|
|
52
|
+
email: data.email,
|
|
53
|
+
password: hashedPassword
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Fire-and-forget for non-critical operations
|
|
57
|
+
this.emailer.sendWelcome(user.email).catch(e => this.logger.error('Welcome email failed', e));
|
|
58
|
+
this.analytics.track('user_created', { userId: user.id });
|
|
59
|
+
|
|
60
|
+
return Ok(toUserDTO(user));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
### Open/Closed Principle (OCP)
|
|
68
|
+
|
|
69
|
+
**Strategy Pattern for Extension**
|
|
70
|
+
```typescript
|
|
71
|
+
// CORRECT: Open for extension, closed for modification
|
|
72
|
+
interface PaymentStrategy {
|
|
73
|
+
readonly name: string;
|
|
74
|
+
process(amount: number): Promise<PaymentResult>;
|
|
75
|
+
validate(data: PaymentData): ValidationResult;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class CreditCardPayment implements PaymentStrategy {
|
|
79
|
+
readonly name = 'credit_card';
|
|
80
|
+
|
|
81
|
+
async process(amount: number): Promise<PaymentResult> {
|
|
82
|
+
// Credit card specific logic
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
validate(data: PaymentData): ValidationResult {
|
|
86
|
+
// Credit card validation
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class PayPalPayment implements PaymentStrategy {
|
|
91
|
+
readonly name = 'paypal';
|
|
92
|
+
// PayPal implementation
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Adding new payment = new class, no modification to existing code
|
|
96
|
+
class ApplePayPayment implements PaymentStrategy {
|
|
97
|
+
readonly name = 'apple_pay';
|
|
98
|
+
// Apple Pay implementation
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class PaymentProcessor {
|
|
102
|
+
private strategies: Map<string, PaymentStrategy>;
|
|
103
|
+
|
|
104
|
+
constructor(strategies: PaymentStrategy[]) {
|
|
105
|
+
this.strategies = new Map(strategies.map(s => [s.name, s]));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Register new strategies without modifying this class
|
|
109
|
+
register(strategy: PaymentStrategy) {
|
|
110
|
+
this.strategies.set(strategy.name, strategy);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async process(method: string, amount: number): Promise<PaymentResult> {
|
|
114
|
+
const strategy = this.strategies.get(method);
|
|
115
|
+
if (!strategy) {
|
|
116
|
+
return { ok: false, error: 'Unknown payment method' };
|
|
117
|
+
}
|
|
118
|
+
return strategy.process(amount);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### Liskov Substitution Principle (LSP)
|
|
126
|
+
|
|
127
|
+
**Proper Interface Segregation**
|
|
128
|
+
```typescript
|
|
129
|
+
// CORRECT: Interfaces match actual capabilities
|
|
130
|
+
interface Readable {
|
|
131
|
+
read(path: string): string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface Writable {
|
|
135
|
+
write(path: string, content: string): void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
interface Deletable {
|
|
139
|
+
delete(path: string): void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
class FileStorage implements Readable, Writable, Deletable {
|
|
143
|
+
read(path: string): string { /* ... */ }
|
|
144
|
+
write(path: string, content: string): void { /* ... */ }
|
|
145
|
+
delete(path: string): void { /* ... */ }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class ReadOnlyStorage implements Readable {
|
|
149
|
+
read(path: string): string { /* ... */ }
|
|
150
|
+
// No write or delete - doesn't claim to support them
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Function declares what it needs
|
|
154
|
+
function backup(storage: Writable, data: string) {
|
|
155
|
+
storage.write('/backup/data.txt', data);
|
|
156
|
+
}
|
|
157
|
+
// ReadOnlyStorage can't be passed - compile error, not runtime error
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### Interface Segregation Principle (ISP)
|
|
163
|
+
|
|
164
|
+
**Segregated Interfaces by Capability**
|
|
165
|
+
```typescript
|
|
166
|
+
// CORRECT: Small, focused interfaces
|
|
167
|
+
interface DocumentReader {
|
|
168
|
+
open(path: string): Document;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
interface DocumentWriter {
|
|
172
|
+
save(doc: Document): void;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface Printable {
|
|
176
|
+
print(doc: Document): void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface Emailable {
|
|
180
|
+
email(doc: Document, to: string): void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface Faxable {
|
|
184
|
+
fax(doc: Document, number: string): void;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface Encryptable {
|
|
188
|
+
encrypt(doc: Document): Document;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Implement only what you support
|
|
192
|
+
class PdfHandler implements DocumentReader, DocumentWriter, Printable, Emailable, Encryptable {
|
|
193
|
+
open(path: string) { /* ... */ }
|
|
194
|
+
save(doc: Document) { /* ... */ }
|
|
195
|
+
print(doc: Document) { /* ... */ }
|
|
196
|
+
email(doc: Document, to: string) { /* ... */ }
|
|
197
|
+
encrypt(doc: Document) { /* ... */ }
|
|
198
|
+
// No fax - we don't claim to support it
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
class LegacyFaxHandler implements Faxable {
|
|
202
|
+
fax(doc: Document, number: string) { /* ... */ }
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### Dependency Inversion Principle (DIP)
|
|
209
|
+
|
|
210
|
+
**Depend on Abstractions**
|
|
211
|
+
```typescript
|
|
212
|
+
// CORRECT: High-level module depends on abstractions
|
|
213
|
+
interface NotificationChannel {
|
|
214
|
+
readonly type: string;
|
|
215
|
+
send(recipient: string, message: string): Promise<Result<void, Error>>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
class SMSChannel implements NotificationChannel {
|
|
219
|
+
readonly type = 'sms';
|
|
220
|
+
|
|
221
|
+
constructor(private client: SMSProvider) {}
|
|
222
|
+
|
|
223
|
+
async send(phone: string, message: string): Promise<Result<void, Error>> {
|
|
224
|
+
try {
|
|
225
|
+
await this.client.sendSMS(phone, message);
|
|
226
|
+
return Ok(undefined);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return Err(error as Error);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
class EmailChannel implements NotificationChannel {
|
|
234
|
+
readonly type = 'email';
|
|
235
|
+
|
|
236
|
+
constructor(private client: EmailProvider) {}
|
|
237
|
+
|
|
238
|
+
async send(email: string, message: string): Promise<Result<void, Error>> {
|
|
239
|
+
// Implementation
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
class NotificationService {
|
|
244
|
+
constructor(
|
|
245
|
+
private channels: Map<string, NotificationChannel>,
|
|
246
|
+
private userRepository: UserRepository,
|
|
247
|
+
private logger: Logger
|
|
248
|
+
) {}
|
|
249
|
+
|
|
250
|
+
async notifyUser(userId: string, message: string): Promise<Result<void, Error>> {
|
|
251
|
+
const user = await this.userRepository.findById(userId);
|
|
252
|
+
if (!user.ok) return user;
|
|
253
|
+
|
|
254
|
+
const results = await Promise.all(
|
|
255
|
+
user.value.preferredChannels.map(async (channelType) => {
|
|
256
|
+
const channel = this.channels.get(channelType);
|
|
257
|
+
if (!channel) {
|
|
258
|
+
this.logger.warn(`Unknown channel: ${channelType}`);
|
|
259
|
+
return Ok(undefined);
|
|
260
|
+
}
|
|
261
|
+
return channel.send(user.value.contactInfo[channelType], message);
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const error = results.find(r => !r.ok);
|
|
266
|
+
return error ?? Ok(undefined);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Composition root - wire dependencies
|
|
271
|
+
const smsChannel = new SMSChannel(new TwilioClient(config.twilioKey));
|
|
272
|
+
const emailChannel = new EmailChannel(new SendGridClient(config.sendgridKey));
|
|
273
|
+
|
|
274
|
+
const notificationService = new NotificationService(
|
|
275
|
+
new Map([
|
|
276
|
+
['sms', smsChannel],
|
|
277
|
+
['email', emailChannel]
|
|
278
|
+
]),
|
|
279
|
+
userRepository,
|
|
280
|
+
logger
|
|
281
|
+
);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Coupling Solutions
|
|
287
|
+
|
|
288
|
+
### Breaking Circular Dependencies
|
|
289
|
+
|
|
290
|
+
**Event-Based Decoupling**
|
|
291
|
+
```typescript
|
|
292
|
+
// CORRECT: Use events to break the cycle
|
|
293
|
+
// user-events.ts
|
|
294
|
+
interface UserEvents {
|
|
295
|
+
onUserDeleting(userId: string): Promise<void>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// order.service.ts - implements event handler
|
|
299
|
+
class OrderService implements UserEvents {
|
|
300
|
+
async onUserDeleting(userId: string) {
|
|
301
|
+
await this.repository.cancelByUserId(userId);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async createOrder(userId: string, items: Item[], creditLimit: number) {
|
|
305
|
+
// Credit limit passed in, no user service dependency
|
|
306
|
+
if (creditLimit < this.calculateTotal(items)) {
|
|
307
|
+
return Err({ type: 'credit_exceeded' });
|
|
308
|
+
}
|
|
309
|
+
return this.repository.create({ userId, items });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// user.service.ts - publishes events
|
|
314
|
+
class UserService {
|
|
315
|
+
constructor(
|
|
316
|
+
private repository: UserRepository,
|
|
317
|
+
private eventBus: EventBus
|
|
318
|
+
) {}
|
|
319
|
+
|
|
320
|
+
async deleteUser(userId: string) {
|
|
321
|
+
await this.eventBus.emit('user.deleting', { userId });
|
|
322
|
+
await this.repository.delete(userId);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Orchestration Layer**
|
|
328
|
+
```typescript
|
|
329
|
+
// CORRECT: Extract coordination to facade
|
|
330
|
+
class UserOrderFacade {
|
|
331
|
+
constructor(
|
|
332
|
+
private userService: UserService,
|
|
333
|
+
private orderService: OrderService
|
|
334
|
+
) {}
|
|
335
|
+
|
|
336
|
+
async createOrderForUser(userId: string, items: Item[]) {
|
|
337
|
+
const user = await this.userService.getUser(userId);
|
|
338
|
+
return this.orderService.createOrder(userId, items, user.creditLimit);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async deleteUserWithOrders(userId: string) {
|
|
342
|
+
await this.orderService.cancelAllUserOrders(userId);
|
|
343
|
+
await this.userService.deleteUser(userId);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
### Eliminating Feature Envy
|
|
351
|
+
|
|
352
|
+
**Move Behavior to Data Owner**
|
|
353
|
+
```typescript
|
|
354
|
+
// CORRECT: Objects own their behavior
|
|
355
|
+
class Order {
|
|
356
|
+
getSubtotal(): number {
|
|
357
|
+
return this.items.reduce((sum, item) => sum + item.getTotal(), 0);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
getShippingCost(): number {
|
|
361
|
+
return this.shippingMethod.calculate(this.getWeight(), this.getShippingAddress());
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
getTax(): number {
|
|
365
|
+
return this.taxCalculator.calculate(this.getSubtotal(), this.getBillingAddress());
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
getTotal(): number {
|
|
369
|
+
return this.getSubtotal() + this.getShippingCost() + this.getTax();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private getWeight(): number {
|
|
373
|
+
return this.items.reduce((sum, item) => sum + item.getWeight(), 0);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
class OrderItem {
|
|
378
|
+
getTotal(): number {
|
|
379
|
+
return this.product.getPrice() * this.quantity;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
getWeight(): number {
|
|
383
|
+
return this.product.getWeight() * this.quantity;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
class Product {
|
|
388
|
+
getPrice(): number {
|
|
389
|
+
const discount = this.getActiveDiscount();
|
|
390
|
+
return this.pricing.basePrice * (1 - discount);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private getActiveDiscount(): number {
|
|
394
|
+
return this.pricing.activePromotions
|
|
395
|
+
.filter(p => p.isValid())
|
|
396
|
+
.reduce((max, p) => Math.max(max, p.discountPercent), 0) / 100;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
class Customer {
|
|
401
|
+
getShippingAddress(): Address {
|
|
402
|
+
return this.addresses.find(a => a.type === 'shipping') ?? this.defaultAddress;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
getBillingAddress(): Address {
|
|
406
|
+
return this.addresses.find(a => a.type === 'billing') ?? this.getShippingAddress();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
formatForShipping(): ShippingLabel {
|
|
410
|
+
return this.profile.formatForShipping(this.getShippingAddress());
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// OrderProcessor is now simple orchestration
|
|
415
|
+
class OrderProcessor {
|
|
416
|
+
async process(order: Order) {
|
|
417
|
+
const total = order.getTotal();
|
|
418
|
+
const shippingLabel = order.customer.formatForShipping();
|
|
419
|
+
// Simple orchestration, not data manipulation
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### Loose Coupling via Injection
|
|
427
|
+
|
|
428
|
+
**Dependency Injection Pattern**
|
|
429
|
+
```typescript
|
|
430
|
+
// CORRECT: All dependencies injected
|
|
431
|
+
interface DataSource {
|
|
432
|
+
query<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
interface CacheService {
|
|
436
|
+
get<T>(key: string): Promise<T | null>;
|
|
437
|
+
set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
interface ReportFormatter {
|
|
441
|
+
format(data: unknown[]): Buffer;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
interface FileStorage {
|
|
445
|
+
upload(path: string, content: Buffer): Promise<string>;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
interface Notifier {
|
|
449
|
+
send(message: string): Promise<void>;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
class ReportGenerator {
|
|
453
|
+
constructor(
|
|
454
|
+
private dataSource: DataSource,
|
|
455
|
+
private cache: CacheService,
|
|
456
|
+
private formatter: ReportFormatter,
|
|
457
|
+
private storage: FileStorage,
|
|
458
|
+
private notifier: Notifier,
|
|
459
|
+
private logger: Logger
|
|
460
|
+
) {}
|
|
461
|
+
|
|
462
|
+
async generateSalesReport(
|
|
463
|
+
startDate: Date,
|
|
464
|
+
endDate: Date
|
|
465
|
+
): Promise<Result<ReportResult, Error>> {
|
|
466
|
+
const cacheKey = `report:sales:${startDate.toISOString()}:${endDate.toISOString()}`;
|
|
467
|
+
|
|
468
|
+
const cached = await this.cache.get<ReportResult>(cacheKey);
|
|
469
|
+
if (cached) {
|
|
470
|
+
this.logger.info('Report served from cache', { cacheKey });
|
|
471
|
+
return Ok(cached);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
const sales = await this.dataSource.query<SaleRecord>(
|
|
476
|
+
'SELECT * FROM sales WHERE date BETWEEN $1 AND $2',
|
|
477
|
+
[startDate, endDate]
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const report = this.formatter.format(sales);
|
|
481
|
+
const url = await this.storage.upload(
|
|
482
|
+
`reports/sales-${Date.now()}.xlsx`,
|
|
483
|
+
report
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
await this.notifier.send(`Sales report generated: ${url}`);
|
|
487
|
+
|
|
488
|
+
const result = { url, recordCount: sales.length };
|
|
489
|
+
await this.cache.set(cacheKey, result, 3600);
|
|
490
|
+
|
|
491
|
+
return Ok(result);
|
|
492
|
+
} catch (error) {
|
|
493
|
+
this.logger.error('Report generation failed', { error });
|
|
494
|
+
return Err(error as Error);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Composition root - wire dependencies
|
|
500
|
+
function createReportGenerator(config: Config): ReportGenerator {
|
|
501
|
+
return new ReportGenerator(
|
|
502
|
+
new PostgresDataSource(config.database),
|
|
503
|
+
new RedisCache(config.redis),
|
|
504
|
+
new ExcelFormatter(),
|
|
505
|
+
new S3Storage(config.s3),
|
|
506
|
+
new SlackNotifier(config.slack),
|
|
507
|
+
new Logger('ReportGenerator')
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// For tests - inject mocks
|
|
512
|
+
const testGenerator = new ReportGenerator(
|
|
513
|
+
mockDataSource,
|
|
514
|
+
mockCache,
|
|
515
|
+
mockFormatter,
|
|
516
|
+
mockStorage,
|
|
517
|
+
mockNotifier,
|
|
518
|
+
mockLogger
|
|
519
|
+
);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Layering Patterns
|
|
525
|
+
|
|
526
|
+
### Proper Layer Separation
|
|
527
|
+
|
|
528
|
+
**Clean Architecture Layers**
|
|
529
|
+
```typescript
|
|
530
|
+
// Layer 1: Controller (HTTP concerns only)
|
|
531
|
+
class ProductController {
|
|
532
|
+
constructor(private productService: ProductService) {}
|
|
533
|
+
|
|
534
|
+
async getProduct(req, res) {
|
|
535
|
+
const result = await this.productService.findById(req.params.id);
|
|
536
|
+
|
|
537
|
+
if (!result.ok) {
|
|
538
|
+
const status = result.error.type === 'not_found' ? 404 : 500;
|
|
539
|
+
return res.status(status).json({ error: result.error.message });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
res.json(result.value);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async updateProduct(req, res) {
|
|
546
|
+
const result = await this.productService.update(
|
|
547
|
+
req.params.id,
|
|
548
|
+
req.body,
|
|
549
|
+
req.user.id // Auth context
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
if (!result.ok) {
|
|
553
|
+
return res.status(400).json({ error: result.error.message });
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
res.json(result.value);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Layer 2: Service (Business logic)
|
|
561
|
+
class ProductService {
|
|
562
|
+
constructor(
|
|
563
|
+
private repository: ProductRepository,
|
|
564
|
+
private inventoryClient: InventoryClient,
|
|
565
|
+
private cacheService: CacheService,
|
|
566
|
+
private auditService: AuditService
|
|
567
|
+
) {}
|
|
568
|
+
|
|
569
|
+
async findById(id: string): Promise<Result<ProductDTO, ServiceError>> {
|
|
570
|
+
const cached = await this.cacheService.get<ProductDTO>(`product:${id}`);
|
|
571
|
+
if (cached) return Ok(cached);
|
|
572
|
+
|
|
573
|
+
const product = await this.repository.findById(id);
|
|
574
|
+
if (!product.ok) return product;
|
|
575
|
+
|
|
576
|
+
const inventory = await this.inventoryClient.getStock(id);
|
|
577
|
+
const enriched = this.enrichWithInventory(product.value, inventory);
|
|
578
|
+
|
|
579
|
+
await this.cacheService.set(`product:${id}`, enriched, 300);
|
|
580
|
+
|
|
581
|
+
return Ok(enriched);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async update(
|
|
585
|
+
id: string,
|
|
586
|
+
data: UpdateProductData,
|
|
587
|
+
userId: string
|
|
588
|
+
): Promise<Result<ProductDTO, ServiceError>> {
|
|
589
|
+
const updated = await this.repository.update(id, data);
|
|
590
|
+
if (!updated.ok) return updated;
|
|
591
|
+
|
|
592
|
+
await this.cacheService.invalidate(`product:${id}`);
|
|
593
|
+
await this.cacheService.invalidate('products:list');
|
|
594
|
+
|
|
595
|
+
await this.auditService.record({
|
|
596
|
+
action: 'UPDATE',
|
|
597
|
+
entity: 'product',
|
|
598
|
+
entityId: id,
|
|
599
|
+
userId,
|
|
600
|
+
data
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
return Ok(toDTO(updated.value));
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
private enrichWithInventory(product: Product, inventory: InventoryData): ProductDTO {
|
|
607
|
+
return {
|
|
608
|
+
...toDTO(product),
|
|
609
|
+
inStock: inventory.quantity > 0,
|
|
610
|
+
availability: this.calculateAvailability(inventory.quantity)
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Layer 3: Repository (Data access)
|
|
616
|
+
class ProductRepository {
|
|
617
|
+
constructor(private db: Database) {}
|
|
618
|
+
|
|
619
|
+
async findById(id: string): Promise<Result<Product, RepositoryError>> {
|
|
620
|
+
const row = await this.db.query(
|
|
621
|
+
'SELECT * FROM products WHERE id = $1',
|
|
622
|
+
[id]
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
if (!row) {
|
|
626
|
+
return Err({ type: 'not_found', message: `Product ${id} not found` });
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return Ok(this.toDomain(row));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
async update(id: string, data: UpdateProductData): Promise<Result<Product, RepositoryError>> {
|
|
633
|
+
const row = await this.db.query(
|
|
634
|
+
'UPDATE products SET name = $1, price = $2 WHERE id = $3 RETURNING *',
|
|
635
|
+
[data.name, data.price, id]
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
return Ok(this.toDomain(row));
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
private toDomain(row: ProductRow): Product {
|
|
642
|
+
return {
|
|
643
|
+
id: row.id,
|
|
644
|
+
name: row.name,
|
|
645
|
+
price: new Money(row.price_cents, row.currency)
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Layer Responsibility Summary**
|
|
652
|
+
|
|
653
|
+
| Layer | Responsibility | Depends On |
|
|
654
|
+
|-------|---------------|------------|
|
|
655
|
+
| Controller | HTTP, request/response, auth | Service |
|
|
656
|
+
| Service | Business logic, orchestration | Repository, Clients |
|
|
657
|
+
| Repository | Data access, mapping | Database |
|
|
658
|
+
| Client | External service communication | External APIs |
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
### Clean Abstraction Boundaries
|
|
663
|
+
|
|
664
|
+
**Repository Handles All ORM Concerns**
|
|
665
|
+
```typescript
|
|
666
|
+
// Pure domain model - no infrastructure concerns
|
|
667
|
+
interface User {
|
|
668
|
+
readonly id: UserId;
|
|
669
|
+
readonly email: Email;
|
|
670
|
+
readonly organizationId: OrganizationId;
|
|
671
|
+
readonly createdAt: Date;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
interface UserWithOrganization extends User {
|
|
675
|
+
readonly organization: Organization;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Repository handles all ORM concerns
|
|
679
|
+
class UserRepository {
|
|
680
|
+
constructor(private orm: TypeORM) {}
|
|
681
|
+
|
|
682
|
+
async findById(id: UserId): Promise<Result<User, NotFoundError>> {
|
|
683
|
+
const entity = await this.orm.findOne(UserEntity, {
|
|
684
|
+
where: { id: id.value, deletedAt: IsNull() }
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
if (!entity) {
|
|
688
|
+
return Err({ type: 'not_found', entity: 'User', id: id.value });
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return Ok(this.toDomain(entity));
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async findWithOrganization(id: UserId): Promise<Result<UserWithOrganization, NotFoundError>> {
|
|
695
|
+
const entity = await this.orm.findOne(UserEntity, {
|
|
696
|
+
where: { id: id.value, deletedAt: IsNull() },
|
|
697
|
+
relations: ['organization']
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
if (!entity) {
|
|
701
|
+
return Err({ type: 'not_found', entity: 'User', id: id.value });
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return Ok({
|
|
705
|
+
...this.toDomain(entity),
|
|
706
|
+
organization: this.toOrganizationDomain(entity.organization)
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// All mapping happens here
|
|
711
|
+
private toDomain(entity: UserEntity): User {
|
|
712
|
+
return {
|
|
713
|
+
id: UserId.from(entity.id),
|
|
714
|
+
email: Email.from(entity.email),
|
|
715
|
+
organizationId: OrganizationId.from(entity.organization_id),
|
|
716
|
+
createdAt: entity.createdAt
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
private toEntity(user: User): Partial<UserEntity> {
|
|
721
|
+
return {
|
|
722
|
+
id: user.id.value,
|
|
723
|
+
email: user.email.value,
|
|
724
|
+
organization_id: user.organizationId.value
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Service layer works with clean domain types
|
|
730
|
+
class UserService {
|
|
731
|
+
constructor(private repository: UserRepository) {}
|
|
732
|
+
|
|
733
|
+
async getUser(id: UserId): Promise<Result<User, ServiceError>> {
|
|
734
|
+
return this.repository.findById(id);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
async getUserWithOrganization(id: UserId): Promise<Result<UserWithOrganization, ServiceError>> {
|
|
738
|
+
return this.repository.findWithOrganization(id);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
### Correct Dependency Direction
|
|
746
|
+
|
|
747
|
+
**Clean Architecture Implementation**
|
|
748
|
+
```typescript
|
|
749
|
+
// domain/order.ts - Pure domain, no infrastructure imports
|
|
750
|
+
class Order {
|
|
751
|
+
readonly id: OrderId;
|
|
752
|
+
readonly items: ReadonlyArray<OrderItem>;
|
|
753
|
+
readonly customerId: CustomerId;
|
|
754
|
+
readonly status: OrderStatus;
|
|
755
|
+
|
|
756
|
+
// Domain logic only - no I/O
|
|
757
|
+
get total(): Money {
|
|
758
|
+
return this.items.reduce(
|
|
759
|
+
(sum, item) => sum.add(item.total),
|
|
760
|
+
Money.zero(this.currency)
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
canBeCancelled(): boolean {
|
|
765
|
+
return this.status === OrderStatus.Pending ||
|
|
766
|
+
this.status === OrderStatus.Processing;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
cancel(): Result<Order, DomainError> {
|
|
770
|
+
if (!this.canBeCancelled()) {
|
|
771
|
+
return Err({
|
|
772
|
+
type: 'cannot_cancel',
|
|
773
|
+
message: `Order in ${this.status} status cannot be cancelled`
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
return Ok(this.withStatus(OrderStatus.Cancelled));
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
private withStatus(status: OrderStatus): Order {
|
|
780
|
+
return new Order({ ...this, status });
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// domain/ports.ts - Interfaces defined by domain
|
|
785
|
+
interface OrderRepository {
|
|
786
|
+
findById(id: OrderId): Promise<Result<Order, NotFoundError>>;
|
|
787
|
+
save(order: Order): Promise<Result<void, PersistenceError>>;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
interface PaymentGateway {
|
|
791
|
+
charge(amount: Money, source: PaymentSource): Promise<Result<PaymentId, PaymentError>>;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
interface NotificationService {
|
|
795
|
+
notifyOrderConfirmation(order: Order): Promise<Result<void, NotificationError>>;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// application/order.service.ts - Orchestrates domain and infrastructure
|
|
799
|
+
class OrderService {
|
|
800
|
+
constructor(
|
|
801
|
+
private repository: OrderRepository,
|
|
802
|
+
private paymentGateway: PaymentGateway,
|
|
803
|
+
private notifications: NotificationService,
|
|
804
|
+
private events: EventBus
|
|
805
|
+
) {}
|
|
806
|
+
|
|
807
|
+
async createOrder(command: CreateOrderCommand): Promise<Result<Order, OrderError>> {
|
|
808
|
+
const order = Order.create(command);
|
|
809
|
+
|
|
810
|
+
const paymentResult = await this.paymentGateway.charge(
|
|
811
|
+
order.total,
|
|
812
|
+
command.paymentSource
|
|
813
|
+
);
|
|
814
|
+
if (!paymentResult.ok) return paymentResult;
|
|
815
|
+
|
|
816
|
+
const saveResult = await this.repository.save(order);
|
|
817
|
+
if (!saveResult.ok) return saveResult;
|
|
818
|
+
|
|
819
|
+
await this.notifications.notifyOrderConfirmation(order);
|
|
820
|
+
await this.events.publish(new OrderCreatedEvent(order));
|
|
821
|
+
|
|
822
|
+
return Ok(order);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// infrastructure/postgres-order-repository.ts - Implements domain interface
|
|
827
|
+
import { OrderRepository } from '../domain/ports';
|
|
828
|
+
import { Order } from '../domain/order';
|
|
829
|
+
|
|
830
|
+
class PostgresOrderRepository implements OrderRepository {
|
|
831
|
+
constructor(private db: PostgresClient) {}
|
|
832
|
+
|
|
833
|
+
async findById(id: OrderId): Promise<Result<Order, NotFoundError>> {
|
|
834
|
+
const row = await this.db.query(
|
|
835
|
+
'SELECT * FROM orders WHERE id = $1',
|
|
836
|
+
[id.value]
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
if (!row) {
|
|
840
|
+
return Err({ type: 'not_found', entity: 'Order', id: id.value });
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return Ok(this.toDomain(row));
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
async save(order: Order): Promise<Result<void, PersistenceError>> {
|
|
847
|
+
await this.db.query(
|
|
848
|
+
'INSERT INTO orders (id, customer_id, status, total) VALUES ($1, $2, $3, $4)',
|
|
849
|
+
[order.id.value, order.customerId.value, order.status, order.total.cents]
|
|
850
|
+
);
|
|
851
|
+
return Ok(undefined);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
private toDomain(row: OrderRow): Order {
|
|
855
|
+
// Mapping logic
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Composition root - wire everything together
|
|
860
|
+
// This is the ONLY place that knows about concrete implementations
|
|
861
|
+
function createOrderService(): OrderService {
|
|
862
|
+
const db = new PostgresClient(config.database);
|
|
863
|
+
const stripe = new StripeClient(config.stripe);
|
|
864
|
+
const sendgrid = new SendGridClient(config.sendgrid);
|
|
865
|
+
|
|
866
|
+
return new OrderService(
|
|
867
|
+
new PostgresOrderRepository(db),
|
|
868
|
+
new StripePaymentGateway(stripe),
|
|
869
|
+
new SendGridNotificationService(sendgrid),
|
|
870
|
+
new EventBus()
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
```
|