mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-10 10:13:49 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bd2716d5d | ||
|
|
c286659960 |
File diff suppressed because one or more lines are too long
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"sessionId": "d43d7ca3-7fe6-40a0-8bf6-92c3848903b8",
|
||||
"eventCount": 13,
|
||||
"lastAnalysisTimestamp": null,
|
||||
"lastAnalysisEventCount": 0
|
||||
}
|
||||
15
.github/workflows/reusable-test.yml
vendored
15
.github/workflows/reusable-test.yml
vendored
@@ -149,21 +149,6 @@ jobs:
|
||||
env:
|
||||
CLAUDE_CODE_PACKAGE_MANAGER: ${{ inputs.package-manager }}
|
||||
|
||||
- name: Run coverage
|
||||
run: npm run coverage
|
||||
continue-on-error: true
|
||||
env:
|
||||
CLAUDE_CODE_PACKAGE_MANAGER: ${{ inputs.package-manager }}
|
||||
|
||||
- name: Upload coverage reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: coverage-${{ inputs.os }}-node${{ inputs.node-version }}-${{ inputs.package-manager }}
|
||||
path: |
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
|
||||
@@ -89,7 +89,7 @@ This repo is the raw code only. The guides explain everything.
|
||||
### v2.0.0-rc.1 — Surface Refresh, Operator Workflows, and ECC 2.0 Alpha (Apr 2026)
|
||||
|
||||
- **Dashboard GUI** — New Tkinter-based desktop application (`ecc_dashboard.py` or `npm run dashboard`) with dark/light theme toggle, font customization, and project logo in header and taskbar.
|
||||
- **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 60 agents, 228 skills, and 75 legacy command shims.
|
||||
- **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 55 agents, 208 skills, and 72 legacy command shims.
|
||||
- **Operator and outbound workflow expansion** — `brand-voice`, `social-graph-ranker`, `connections-optimizer`, `customer-billing-ops`, `ecc-tools-cost-audit`, `google-workspace-ops`, `project-flow-ops`, and `workspace-surface-audit` round out the operator lane.
|
||||
- **Media and launch tooling** — `manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system.
|
||||
- **Framework and product surface growth** — `nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone.
|
||||
|
||||
@@ -1,798 +0,0 @@
|
||||
{
|
||||
"generated": "2026-05-14T18:36:38.210Z",
|
||||
"totalCommands": 75,
|
||||
"commands": [
|
||||
{
|
||||
"command": "aside",
|
||||
"description": "Aside Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/aside.md"
|
||||
},
|
||||
{
|
||||
"command": "auto-update",
|
||||
"description": "Auto Update",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/auto-update.md"
|
||||
},
|
||||
{
|
||||
"command": "build-fix",
|
||||
"description": "Build and Fix",
|
||||
"type": "refactoring",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/build-fix.md"
|
||||
},
|
||||
{
|
||||
"command": "checkpoint",
|
||||
"description": "Checkpoint Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/checkpoint.md"
|
||||
},
|
||||
{
|
||||
"command": "code-review",
|
||||
"description": "Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/code-review.md"
|
||||
},
|
||||
{
|
||||
"command": "cost-report",
|
||||
"description": "Cost Report",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/cost-report.md"
|
||||
},
|
||||
{
|
||||
"command": "cpp-build",
|
||||
"description": "C++ Build and Fix",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"cpp-coding-standards"
|
||||
],
|
||||
"path": "commands/cpp-build.md"
|
||||
},
|
||||
{
|
||||
"command": "cpp-review",
|
||||
"description": "C++ Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"cpp-coding-standards",
|
||||
"cpp-testing"
|
||||
],
|
||||
"path": "commands/cpp-review.md"
|
||||
},
|
||||
{
|
||||
"command": "cpp-test",
|
||||
"description": "C++ TDD Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"cpp-testing",
|
||||
"tdd-workflow"
|
||||
],
|
||||
"path": "commands/cpp-test.md"
|
||||
},
|
||||
{
|
||||
"command": "ecc-guide",
|
||||
"description": "/ecc-guide",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"ecc-guide",
|
||||
"security-scan"
|
||||
],
|
||||
"path": "commands/ecc-guide.md"
|
||||
},
|
||||
{
|
||||
"command": "evolve",
|
||||
"description": "Analyze instincts and suggest or generate evolved structures",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"continuous-learning-v2"
|
||||
],
|
||||
"path": "commands/evolve.md"
|
||||
},
|
||||
{
|
||||
"command": "fastapi-review",
|
||||
"description": "FastAPI Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/fastapi-review.md"
|
||||
},
|
||||
{
|
||||
"command": "feature-dev",
|
||||
"description": "",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/feature-dev.md"
|
||||
},
|
||||
{
|
||||
"command": "flutter-build",
|
||||
"description": "Flutter Build and Fix",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"flutter-dart-code-review"
|
||||
],
|
||||
"path": "commands/flutter-build.md"
|
||||
},
|
||||
{
|
||||
"command": "flutter-review",
|
||||
"description": "Flutter Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"flutter-dart-code-review"
|
||||
],
|
||||
"path": "commands/flutter-review.md"
|
||||
},
|
||||
{
|
||||
"command": "flutter-test",
|
||||
"description": "Flutter Test",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"flutter-dart-code-review"
|
||||
],
|
||||
"path": "commands/flutter-test.md"
|
||||
},
|
||||
{
|
||||
"command": "gan-build",
|
||||
"description": "",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/gan-build.md"
|
||||
},
|
||||
{
|
||||
"command": "gan-design",
|
||||
"description": "",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/gan-design.md"
|
||||
},
|
||||
{
|
||||
"command": "go-build",
|
||||
"description": "Go Build and Fix",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"golang-patterns"
|
||||
],
|
||||
"path": "commands/go-build.md"
|
||||
},
|
||||
{
|
||||
"command": "go-review",
|
||||
"description": "Go Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"golang-patterns",
|
||||
"golang-testing"
|
||||
],
|
||||
"path": "commands/go-review.md"
|
||||
},
|
||||
{
|
||||
"command": "go-test",
|
||||
"description": "Go TDD Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"golang-testing",
|
||||
"tdd-workflow"
|
||||
],
|
||||
"path": "commands/go-test.md"
|
||||
},
|
||||
{
|
||||
"command": "gradle-build",
|
||||
"description": "Gradle Build Fix",
|
||||
"type": "build",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/gradle-build.md"
|
||||
},
|
||||
{
|
||||
"command": "harness-audit",
|
||||
"description": "Harness Audit Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/harness-audit.md"
|
||||
},
|
||||
{
|
||||
"command": "hookify-configure",
|
||||
"description": "",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/hookify-configure.md"
|
||||
},
|
||||
{
|
||||
"command": "hookify-help",
|
||||
"description": "",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/hookify-help.md"
|
||||
},
|
||||
{
|
||||
"command": "hookify-list",
|
||||
"description": "",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/hookify-list.md"
|
||||
},
|
||||
{
|
||||
"command": "hookify",
|
||||
"description": "",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/hookify.md"
|
||||
},
|
||||
{
|
||||
"command": "instinct-export",
|
||||
"description": "Export instincts from project/global scope to a file",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/instinct-export.md"
|
||||
},
|
||||
{
|
||||
"command": "instinct-import",
|
||||
"description": "Import instincts from file or URL into project/global scope",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"continuous-learning-v2"
|
||||
],
|
||||
"path": "commands/instinct-import.md"
|
||||
},
|
||||
{
|
||||
"command": "instinct-status",
|
||||
"description": "Show learned instincts (project + global) with confidence",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"continuous-learning-v2"
|
||||
],
|
||||
"path": "commands/instinct-status.md"
|
||||
},
|
||||
{
|
||||
"command": "jira",
|
||||
"description": "Jira Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"jira-integration"
|
||||
],
|
||||
"path": "commands/jira.md"
|
||||
},
|
||||
{
|
||||
"command": "kotlin-build",
|
||||
"description": "Kotlin Build and Fix",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"kotlin-patterns"
|
||||
],
|
||||
"path": "commands/kotlin-build.md"
|
||||
},
|
||||
{
|
||||
"command": "kotlin-review",
|
||||
"description": "Kotlin Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"kotlin-patterns",
|
||||
"kotlin-testing"
|
||||
],
|
||||
"path": "commands/kotlin-review.md"
|
||||
},
|
||||
{
|
||||
"command": "kotlin-test",
|
||||
"description": "Kotlin TDD Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"kotlin-testing",
|
||||
"tdd-workflow"
|
||||
],
|
||||
"path": "commands/kotlin-test.md"
|
||||
},
|
||||
{
|
||||
"command": "learn-eval",
|
||||
"description": "\"Under 130 characters\"",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/learn-eval.md"
|
||||
},
|
||||
{
|
||||
"command": "learn",
|
||||
"description": "/learn - Extract Reusable Patterns",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/learn.md"
|
||||
},
|
||||
{
|
||||
"command": "loop-start",
|
||||
"description": "Loop Start Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/loop-start.md"
|
||||
},
|
||||
{
|
||||
"command": "loop-status",
|
||||
"description": "Loop Status Command",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/loop-status.md"
|
||||
},
|
||||
{
|
||||
"command": "model-route",
|
||||
"description": "Model Route Command",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/model-route.md"
|
||||
},
|
||||
{
|
||||
"command": "multi-backend",
|
||||
"description": "Backend - Backend-Focused Development",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/multi-backend.md"
|
||||
},
|
||||
{
|
||||
"command": "multi-execute",
|
||||
"description": "Execute - Multi-Model Collaborative Execution",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/multi-execute.md"
|
||||
},
|
||||
{
|
||||
"command": "multi-frontend",
|
||||
"description": "Frontend - Frontend-Focused Development",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/multi-frontend.md"
|
||||
},
|
||||
{
|
||||
"command": "multi-plan",
|
||||
"description": "Plan - Multi-Model Collaborative Planning",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"accessibility"
|
||||
],
|
||||
"path": "commands/multi-plan.md"
|
||||
},
|
||||
{
|
||||
"command": "multi-workflow",
|
||||
"description": "Workflow - Multi-Model Collaborative Development",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/multi-workflow.md"
|
||||
},
|
||||
{
|
||||
"command": "plan-prd",
|
||||
"description": "PRD Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/plan-prd.md"
|
||||
},
|
||||
{
|
||||
"command": "plan",
|
||||
"description": "Plan Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/plan.md"
|
||||
},
|
||||
{
|
||||
"command": "pm2",
|
||||
"description": "PM2 Init",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/pm2.md"
|
||||
},
|
||||
{
|
||||
"command": "pr",
|
||||
"description": "Create Pull Request",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/pr.md"
|
||||
},
|
||||
{
|
||||
"command": "project-init",
|
||||
"description": "/project-init",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"ecc-guide"
|
||||
],
|
||||
"path": "commands/project-init.md"
|
||||
},
|
||||
{
|
||||
"command": "projects",
|
||||
"description": "List known projects and their instinct statistics",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"continuous-learning-v2"
|
||||
],
|
||||
"path": "commands/projects.md"
|
||||
},
|
||||
{
|
||||
"command": "promote",
|
||||
"description": "Promote project-scoped instincts to global scope",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"continuous-learning-v2"
|
||||
],
|
||||
"path": "commands/promote.md"
|
||||
},
|
||||
{
|
||||
"command": "prp-commit",
|
||||
"description": "Smart Commit",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/prp-commit.md"
|
||||
},
|
||||
{
|
||||
"command": "prp-implement",
|
||||
"description": "PRP Implement",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/prp-implement.md"
|
||||
},
|
||||
{
|
||||
"command": "prp-plan",
|
||||
"description": "PRP Plan",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/prp-plan.md"
|
||||
},
|
||||
{
|
||||
"command": "prp-pr",
|
||||
"description": "Create Pull Request",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/prp-pr.md"
|
||||
},
|
||||
{
|
||||
"command": "prp-prd",
|
||||
"description": "Product Requirements Document Generator",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/prp-prd.md"
|
||||
},
|
||||
{
|
||||
"command": "prune",
|
||||
"description": "Delete pending instincts older than 30 days that were never promoted",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"continuous-learning-v2"
|
||||
],
|
||||
"path": "commands/prune.md"
|
||||
},
|
||||
{
|
||||
"command": "python-review",
|
||||
"description": "Python Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"python-patterns",
|
||||
"python-testing"
|
||||
],
|
||||
"path": "commands/python-review.md"
|
||||
},
|
||||
{
|
||||
"command": "quality-gate",
|
||||
"description": "Quality Gate Command",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/quality-gate.md"
|
||||
},
|
||||
{
|
||||
"command": "refactor-clean",
|
||||
"description": "Refactor Clean",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/refactor-clean.md"
|
||||
},
|
||||
{
|
||||
"command": "resume-session",
|
||||
"description": "Resume Session Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/resume-session.md"
|
||||
},
|
||||
{
|
||||
"command": "review-pr",
|
||||
"description": "",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/review-pr.md"
|
||||
},
|
||||
{
|
||||
"command": "rust-build",
|
||||
"description": "Rust Build and Fix",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"rust-patterns"
|
||||
],
|
||||
"path": "commands/rust-build.md"
|
||||
},
|
||||
{
|
||||
"command": "rust-review",
|
||||
"description": "Rust Code Review",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"rust-patterns",
|
||||
"rust-testing"
|
||||
],
|
||||
"path": "commands/rust-review.md"
|
||||
},
|
||||
{
|
||||
"command": "rust-test",
|
||||
"description": "Rust TDD Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"rust-patterns",
|
||||
"rust-testing"
|
||||
],
|
||||
"path": "commands/rust-test.md"
|
||||
},
|
||||
{
|
||||
"command": "santa-loop",
|
||||
"description": "Santa Loop",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/santa-loop.md"
|
||||
},
|
||||
{
|
||||
"command": "save-session",
|
||||
"description": "Save Session Command",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/save-session.md"
|
||||
},
|
||||
{
|
||||
"command": "security-scan",
|
||||
"description": "Security Scan Command",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [
|
||||
"security-scan"
|
||||
],
|
||||
"path": "commands/security-scan.md"
|
||||
},
|
||||
{
|
||||
"command": "sessions",
|
||||
"description": "Sessions Command",
|
||||
"type": "general",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/sessions.md"
|
||||
},
|
||||
{
|
||||
"command": "setup-pm",
|
||||
"description": "Package Manager Setup",
|
||||
"type": "build",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/setup-pm.md"
|
||||
},
|
||||
{
|
||||
"command": "skill-create",
|
||||
"description": "Analyze local git history to extract coding patterns and generate SKILL.md files. Local version of the Skill Creator GitHub App.",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/skill-create.md"
|
||||
},
|
||||
{
|
||||
"command": "skill-health",
|
||||
"description": "Show skill portfolio health dashboard with charts and analytics",
|
||||
"type": "review",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/skill-health.md"
|
||||
},
|
||||
{
|
||||
"command": "test-coverage",
|
||||
"description": "Test Coverage",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/test-coverage.md"
|
||||
},
|
||||
{
|
||||
"command": "update-codemaps",
|
||||
"description": "Update Codemaps",
|
||||
"type": "planning",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/update-codemaps.md"
|
||||
},
|
||||
{
|
||||
"command": "update-docs",
|
||||
"description": "Update Documentation",
|
||||
"type": "testing",
|
||||
"primaryAgents": [],
|
||||
"allAgents": [],
|
||||
"skills": [],
|
||||
"path": "commands/update-docs.md"
|
||||
}
|
||||
],
|
||||
"statistics": {
|
||||
"byType": {
|
||||
"testing": 52,
|
||||
"refactoring": 1,
|
||||
"review": 11,
|
||||
"build": 2,
|
||||
"general": 8,
|
||||
"planning": 1
|
||||
},
|
||||
"topAgents": [],
|
||||
"topSkills": [
|
||||
{
|
||||
"skill": "continuous-learning-v2",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"skill": "tdd-workflow",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"skill": "flutter-dart-code-review",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"skill": "rust-patterns",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"skill": "cpp-coding-standards",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"skill": "cpp-testing",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"skill": "ecc-guide",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"skill": "security-scan",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"skill": "golang-patterns",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"skill": "golang-testing",
|
||||
"count": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,8 @@
|
||||
"skills/database-migrations",
|
||||
"skills/jpa-patterns",
|
||||
"skills/mysql-patterns",
|
||||
"skills/postgres-patterns"
|
||||
"skills/postgres-patterns",
|
||||
"skills/prisma-patterns"
|
||||
],
|
||||
"targets": [
|
||||
"claude",
|
||||
|
||||
@@ -285,8 +285,6 @@
|
||||
"postinstall": "echo '\\n ecc-universal installed!\\n Run: npx ecc typescript\\n Compat: npx ecc-install typescript\\n Docs: https://github.com/affaan-m/everything-claude-code\\n'",
|
||||
"catalog:check": "node scripts/ci/catalog.js --text",
|
||||
"catalog:sync": "node scripts/ci/catalog.js --write --text",
|
||||
"command-registry:generate": "node scripts/ci/generate-command-registry.js",
|
||||
"command-registry:write": "node scripts/ci/generate-command-registry.js --write",
|
||||
"lint": "eslint . && markdownlint '**/*.md' --ignore node_modules",
|
||||
"harness:adapters": "node scripts/harness-adapter-compliance.js",
|
||||
"harness:audit": "node scripts/harness-audit.js",
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Generate Command → Agent/Skill Registry
|
||||
*
|
||||
* Scans all command markdown files and extracts:
|
||||
* - Command name/description
|
||||
* - Primary agent(s) referenced
|
||||
* - Skills referenced
|
||||
* - Command type (workflow, testing, review, etc.)
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/ci/generate-command-registry.js
|
||||
* node scripts/ci/generate-command-registry.js --json
|
||||
* node scripts/ci/generate-command-registry.js --write
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.join(__dirname, '../..');
|
||||
const COMMANDS_DIR = path.join(ROOT, 'commands');
|
||||
const AGENTS_DIR = path.join(ROOT, 'agents');
|
||||
const OUTPUT_PATH = path.join(ROOT, 'docs', 'COMMAND-REGISTRY.json');
|
||||
const WRITE_MODE = process.argv.includes('--write');
|
||||
const OUTPUT_JSON = process.argv.includes('--json');
|
||||
|
||||
const KNOWN_AGENTS = new Set();
|
||||
const KNOWN_SKILLS = new Set();
|
||||
|
||||
// Scan agents directory for known agents
|
||||
function scanKnownAgents() {
|
||||
if (!fs.existsSync(AGENTS_DIR)) return;
|
||||
|
||||
const files = fs.readdirSync(AGENTS_DIR, { withFileTypes: true });
|
||||
files.forEach(entry => {
|
||||
if (entry.isFile() && entry.name.endsWith('.md')) {
|
||||
const agentName = entry.name.replace('.md', '');
|
||||
KNOWN_AGENTS.add(agentName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Scan skills directory for known skills
|
||||
function scanKnownSkills() {
|
||||
const skillsDir = path.join(ROOT, 'skills');
|
||||
if (!fs.existsSync(skillsDir)) return;
|
||||
|
||||
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
||||
entries.forEach(entry => {
|
||||
if (entry.isDirectory() && fs.existsSync(path.join(skillsDir, entry.name, 'SKILL.md'))) {
|
||||
KNOWN_SKILLS.add(entry.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extract agents and skills from markdown content
|
||||
function extractReferences(content) {
|
||||
const agents = new Set();
|
||||
const skills = new Set();
|
||||
|
||||
// Pattern: @agent-name or agent-name in code blocks, lists, or descriptions
|
||||
const agentPatterns = [
|
||||
/@([a-z][a-z0-9-]*)/gi, // @agent-name
|
||||
/agent:\s*([a-z][a-z0-9-]*)/gi, // agent: name
|
||||
/subagent(?:_type)?:\s*["']?([a-z][a-z0-9-]*)/gi, // subagent_type: "name"
|
||||
];
|
||||
|
||||
// Pattern: /skill-name or /command-name
|
||||
const skillPatterns = [
|
||||
/\/([a-z][a-z0-9-]*)/gi, // /skill-name
|
||||
/skill:\s*\/?([a-z][a-z0-9-]*)/gi, // skill: /name or skill: name
|
||||
];
|
||||
|
||||
// Extract agents
|
||||
agentPatterns.forEach(pattern => {
|
||||
const matches = content.matchAll(pattern);
|
||||
for (const match of matches) {
|
||||
const ref = match[1];
|
||||
if (KNOWN_AGENTS.has(ref)) {
|
||||
agents.add(ref);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Extract skills
|
||||
skillPatterns.forEach(pattern => {
|
||||
const matches = content.matchAll(pattern);
|
||||
for (const match of matches) {
|
||||
const ref = match[1];
|
||||
if (KNOWN_SKILLS.has(ref)) {
|
||||
skills.add(ref);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
agents: Array.from(agents).sort(),
|
||||
skills: Array.from(skills).sort()
|
||||
};
|
||||
}
|
||||
|
||||
// Infer command type from content
|
||||
function inferCommandType(content, filename) {
|
||||
const lower = content.toLowerCase();
|
||||
|
||||
if (lower.includes('test') || lower.includes('tdd') || lower.includes('coverage')) {
|
||||
return 'testing';
|
||||
}
|
||||
if (lower.includes('review') || lower.includes('audit') || lower.includes('quality')) {
|
||||
return 'review';
|
||||
}
|
||||
if (lower.includes('plan') || lower.includes('design') || lower.includes('architecture')) {
|
||||
return 'planning';
|
||||
}
|
||||
if (lower.includes('refactor') || lower.includes('clean') || lower.includes('simplify')) {
|
||||
return 'refactoring';
|
||||
}
|
||||
if (lower.includes('build') || lower.includes('compile') || lower.includes('setup')) {
|
||||
return 'build';
|
||||
}
|
||||
if (filename.startsWith('multi-')) {
|
||||
return 'orchestration';
|
||||
}
|
||||
|
||||
return 'general';
|
||||
}
|
||||
|
||||
// Process single command file
|
||||
function processCommandFile(filename) {
|
||||
const filePath = path.join(COMMANDS_DIR, filename);
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// Extract description from frontmatter or first heading
|
||||
let description = '';
|
||||
const frontmatterMatch = content.match(/^---\n[\s\S]*?\ndescription:\s*(.+?)\n[\s\S]*?^---/m);
|
||||
if (frontmatterMatch) {
|
||||
description = frontmatterMatch[1].trim();
|
||||
} else {
|
||||
const headingMatch = content.match(/^#\s+(.+)$/m);
|
||||
if (headingMatch) {
|
||||
description = headingMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
const commandName = filename.replace('.md', '');
|
||||
const references = extractReferences(content);
|
||||
const type = inferCommandType(content, filename);
|
||||
|
||||
return {
|
||||
command: commandName,
|
||||
description,
|
||||
type,
|
||||
primaryAgents: references.agents.slice(0, 3), // Top 3 agents
|
||||
allAgents: references.agents,
|
||||
skills: references.skills,
|
||||
path: `commands/${filename}`
|
||||
};
|
||||
}
|
||||
|
||||
// Generate full registry
|
||||
function generateRegistry() {
|
||||
scanKnownAgents();
|
||||
scanKnownSkills();
|
||||
|
||||
if (!fs.existsSync(COMMANDS_DIR)) {
|
||||
console.error('commands/ directory not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(COMMANDS_DIR, { withFileTypes: true })
|
||||
.filter(entry => entry.isFile() && entry.name.endsWith('.md'))
|
||||
.map(entry => entry.name)
|
||||
.sort();
|
||||
|
||||
const registry = {
|
||||
generated: new Date().toISOString(),
|
||||
totalCommands: files.length,
|
||||
commands: files.map(processCommandFile)
|
||||
};
|
||||
|
||||
// Add statistics
|
||||
const typeCounts = {};
|
||||
const agentUsage = {};
|
||||
const skillUsage = {};
|
||||
|
||||
registry.commands.forEach(cmd => {
|
||||
typeCounts[cmd.type] = (typeCounts[cmd.type] || 0) + 1;
|
||||
cmd.allAgents.forEach(agent => {
|
||||
agentUsage[agent] = (agentUsage[agent] || 0) + 1;
|
||||
});
|
||||
cmd.skills.forEach(skill => {
|
||||
skillUsage[skill] = (skillUsage[skill] || 0) + 1;
|
||||
});
|
||||
});
|
||||
|
||||
registry.statistics = {
|
||||
byType: typeCounts,
|
||||
topAgents: Object.entries(agentUsage)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 10)
|
||||
.map(([agent, count]) => ({ agent, count })),
|
||||
topSkills: Object.entries(skillUsage)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 10)
|
||||
.map(([skill, count]) => ({ skill, count }))
|
||||
};
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
// Main execution
|
||||
function main() {
|
||||
const registry = generateRegistry();
|
||||
|
||||
if (OUTPUT_JSON) {
|
||||
console.log(JSON.stringify(registry, null, 2));
|
||||
} else {
|
||||
console.log('\n📊 Command Registry Statistics\n');
|
||||
console.log(`Total commands: ${registry.totalCommands}`);
|
||||
console.log('\nBy type:');
|
||||
Object.entries(registry.statistics.byType)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.forEach(([type, count]) => {
|
||||
console.log(` ${type}: ${count}`);
|
||||
});
|
||||
|
||||
console.log('\nTop 10 agents:');
|
||||
registry.statistics.topAgents.forEach(({ agent, count }) => {
|
||||
console.log(` ${agent}: ${count} commands`);
|
||||
});
|
||||
|
||||
console.log('\nTop 10 skills:');
|
||||
registry.statistics.topSkills.forEach(({ skill, count }) => {
|
||||
console.log(` ${skill}: ${count} commands`);
|
||||
});
|
||||
|
||||
console.log(`\n📄 Generated: ${registry.generated}`);
|
||||
}
|
||||
|
||||
if (WRITE_MODE) {
|
||||
fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
|
||||
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(registry, null, 2) + '\n');
|
||||
console.log(`\n✅ Registry written to: ${OUTPUT_PATH}`);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
371
skills/prisma-patterns/SKILL.md
Normal file
371
skills/prisma-patterns/SKILL.md
Normal file
@@ -0,0 +1,371 @@
|
||||
---
|
||||
name: prisma-patterns
|
||||
description: Prisma ORM patterns for TypeScript backends — schema design, query optimization, transactions, pagination, and critical traps like updateMany returning count not records, $transaction timeouts, migrate dev resetting the DB, @updatedAt skipped on bulk writes, and serverless connection exhaustion.
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Prisma Patterns
|
||||
|
||||
Production patterns and non-obvious traps for Prisma ORM in TypeScript backends.
|
||||
Tested against Prisma 5.x and 6.x. Some behaviors differ from Prisma 4.
|
||||
|
||||
Check the Prisma version before applying version-specific patterns:
|
||||
|
||||
```bash
|
||||
npx prisma --version
|
||||
```
|
||||
|
||||
Prisma 5 introduced `relationJoins`, which can load relations via JOIN rather than separate queries depending on query strategy and configuration. The `omit` field modifier and `prisma.$extends` Client Extensions API were also added. Note: `relationJoins` can cause row explosion on large 1:N relations or deep nested `include` — benchmark both approaches when relations may return many rows per parent.
|
||||
|
||||
## When to Activate
|
||||
|
||||
- Designing or modifying Prisma schema models and relations
|
||||
- Writing queries, transactions, or pagination logic
|
||||
- Using `updateMany`, `deleteMany`, or any bulk operation
|
||||
- Running or planning database migrations
|
||||
- Deploying to serverless environments (Vercel, Lambda, Cloudflare Workers)
|
||||
- Implementing soft delete or multi-tenant row filtering
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### ID Strategy
|
||||
|
||||
| Strategy | Use When | Avoid When |
|
||||
|---|---|---|
|
||||
| `@default(cuid())` | Default choice — URL-safe, sortable, no collisions | Sequential IDs needed for external systems |
|
||||
| `@default(uuid())` | Interoperability with non-Prisma systems required | High-write tables (random UUIDs fragment B-tree indexes) |
|
||||
| `@default(autoincrement())` | Internal join tables, audit logs | Public-facing IDs (exposes record count) |
|
||||
|
||||
### Schema Defaults
|
||||
|
||||
```prisma
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique // @unique already creates an index — no @@index needed
|
||||
name String
|
||||
role Role @default(USER)
|
||||
posts Post[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
@@index([createdAt])
|
||||
@@index([deletedAt, createdAt]) // composite for soft-delete + sort queries
|
||||
}
|
||||
```
|
||||
|
||||
- Add `@@index` on every foreign key and column used in `WHERE` or `ORDER BY`.
|
||||
- Declare `deletedAt DateTime?` upfront when soft delete is a foreseeable requirement — adding it later requires a migration on a live table.
|
||||
- `updatedAt @updatedAt` is set automatically by Prisma on `update` and `upsert` only (see Anti-Patterns for bulk update trap).
|
||||
|
||||
### `include` vs `select`
|
||||
|
||||
| | `include` | `select` |
|
||||
|---|---|---|
|
||||
| Returns | All scalar fields + specified relations | Only specified fields |
|
||||
| Use when | You need most fields plus a relation | Hot paths, large tables, avoiding over-fetch |
|
||||
| Performance | May over-fetch on wide tables | Minimal payload, faster on large datasets |
|
||||
| Prisma 5 note | Uses JOIN by default (`relationJoins`) | Same |
|
||||
|
||||
```ts
|
||||
// include — all columns + relation
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
include: { posts: { select: { id: true, title: true } } },
|
||||
});
|
||||
|
||||
// select — explicit allowlist
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, email: true, name: true },
|
||||
});
|
||||
```
|
||||
|
||||
Never return raw Prisma entities from API responses — map to response DTOs to control exposed fields:
|
||||
|
||||
```ts
|
||||
// BAD: leaks passwordHash, deletedAt, internal fields
|
||||
return await prisma.user.findUniqueOrThrow({ where: { id } });
|
||||
|
||||
// GOOD: explicit DTO mapping
|
||||
const user = await prisma.user.findUniqueOrThrow({ where: { id } });
|
||||
return { id: user.id, name: user.name, email: user.email };
|
||||
```
|
||||
|
||||
### Transaction Form Selection
|
||||
|
||||
| Situation | Use |
|
||||
|---|---|
|
||||
| Independent operations, no inter-dependency | Array form |
|
||||
| Later step depends on earlier result | Interactive form |
|
||||
| External calls (email, HTTP) involved | Outside transaction entirely |
|
||||
|
||||
```ts
|
||||
// Array form — batched in one round trip
|
||||
const [user, post] = await prisma.$transaction([
|
||||
prisma.user.update({ where: { id }, data: { name } }),
|
||||
prisma.post.create({ data: { title, authorId: id } }),
|
||||
]);
|
||||
|
||||
// Interactive form — use tx client only, never the outer prisma client
|
||||
const post = await prisma.$transaction(async (tx) => {
|
||||
const user = await tx.user.findUniqueOrThrow({ where: { id } });
|
||||
if (user.role !== 'ADMIN') throw new Error('Forbidden');
|
||||
return tx.post.create({ data: { title, authorId: user.id } });
|
||||
});
|
||||
```
|
||||
|
||||
### PrismaClient Singleton
|
||||
|
||||
Each `PrismaClient` instance opens its own connection pool. Instantiate once.
|
||||
|
||||
```ts
|
||||
// lib/prisma.ts
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
|
||||
|
||||
export const prisma =
|
||||
globalForPrisma.prisma ??
|
||||
new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
|
||||
```
|
||||
|
||||
The `globalThis` pattern prevents duplicate instances during hot reload (Next.js, nodemon, ts-node-dev).
|
||||
|
||||
### N+1 Problem
|
||||
|
||||
Loading relations inside a loop issues one query per row.
|
||||
|
||||
```ts
|
||||
// BAD: N+1 — one extra query per user
|
||||
const users = await prisma.user.findMany();
|
||||
for (const user of users) {
|
||||
const posts = await prisma.post.findMany({ where: { authorId: user.id } });
|
||||
}
|
||||
|
||||
// GOOD: single query
|
||||
const users = await prisma.user.findMany({ include: { posts: true } });
|
||||
```
|
||||
|
||||
With Prisma 5+ `relationJoins`, the `include` form uses a single JOIN. On large 1:N sets this may increase result set size — benchmark both approaches if the relation can return many rows per parent.
|
||||
|
||||
## Code Examples
|
||||
|
||||
### Cursor Pagination (preferred for feeds and large datasets)
|
||||
|
||||
```ts
|
||||
async function getPosts(cursor?: string, limit = 20) {
|
||||
const items = await prisma.post.findMany({
|
||||
where: { published: true },
|
||||
orderBy: [
|
||||
{ createdAt: 'desc' },
|
||||
{ id: 'desc' }, // secondary sort prevents unstable pagination on duplicate timestamps
|
||||
],
|
||||
take: limit + 1,
|
||||
...(cursor && { cursor: { id: cursor }, skip: 1 }),
|
||||
});
|
||||
|
||||
const hasNextPage = items.length > limit;
|
||||
if (hasNextPage) items.pop();
|
||||
|
||||
return { items, nextCursor: hasNextPage ? items[items.length - 1].id : null };
|
||||
}
|
||||
```
|
||||
|
||||
Fetch `limit + 1` and pop — canonical way to detect `hasNextPage` without an extra count query. Always include a unique field (e.g. `id`) as a secondary `orderBy` to prevent unstable pagination when multiple rows share the same timestamp. Use offset pagination only when users need to jump to arbitrary pages (admin tables).
|
||||
|
||||
### Soft Delete
|
||||
|
||||
```ts
|
||||
// Always filter explicitly — do not rely on middleware (hides behavior, hard to debug)
|
||||
const activeUsers = await prisma.user.findMany({ where: { deletedAt: null } });
|
||||
|
||||
await prisma.user.update({ where: { id }, data: { deletedAt: new Date() } });
|
||||
await prisma.user.update({ where: { id }, data: { deletedAt: null } }); // restore
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```ts
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
try {
|
||||
await prisma.user.create({ data: { email } });
|
||||
} catch (e) {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') throw new ConflictError('Email already exists');
|
||||
if (e.code === 'P2025') throw new NotFoundError('Record not found');
|
||||
if (e.code === 'P2003') throw new BadRequestError('Referenced record does not exist');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
```
|
||||
|
||||
Common codes: `P2002` unique violation · `P2025` not found · `P2003` foreign key violation.
|
||||
|
||||
Catch at the service boundary and translate to domain errors. Never expose raw Prisma messages to API consumers.
|
||||
|
||||
### Connection Pool — Serverless
|
||||
|
||||
Embed connection params directly in `DATABASE_URL` — string concatenation breaks if the URL already has query parameters (e.g. `?schema=public`):
|
||||
|
||||
```bash
|
||||
# .env — preferred: embed params in the URL
|
||||
DATABASE_URL="postgresql://user:pass@host/db?connection_limit=1&pool_timeout=20"
|
||||
|
||||
# With an external pooler (PgBouncer, Supabase pooler)
|
||||
DATABASE_URL="postgresql://user:pass@host/db?pgbouncer=true&connection_limit=1"
|
||||
```
|
||||
|
||||
```ts
|
||||
// Vercel, AWS Lambda, and similar serverless runtimes: cap pool to 1 per instance
|
||||
// connection_limit and pool_timeout are controlled via DATABASE_URL
|
||||
const prisma = new PrismaClient();
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### `updateMany` returns a count, not records
|
||||
|
||||
```ts
|
||||
// BAD: result is { count: 2 } — users[0] is undefined
|
||||
const users = await prisma.user.updateMany({ where: { role: 'GUEST' }, data: { role: 'USER' } });
|
||||
|
||||
// GOOD: capture IDs first, then update, then fetch only the affected rows
|
||||
const targets = await prisma.user.findMany({
|
||||
where: { role: 'GUEST' },
|
||||
select: { id: true },
|
||||
});
|
||||
const ids = targets.map((u) => u.id);
|
||||
await prisma.user.updateMany({ where: { id: { in: ids } }, data: { role: 'USER' } });
|
||||
const updated = await prisma.user.findMany({ where: { id: { in: ids } } });
|
||||
```
|
||||
|
||||
Same applies to `deleteMany` — returns `{ count: n }`, never the deleted rows.
|
||||
|
||||
### `$transaction` interactive form times out after 5 seconds
|
||||
|
||||
```ts
|
||||
// BAD: external call inside transaction exceeds 5s default → "Transaction already closed"
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const user = await tx.user.findUniqueOrThrow({ where: { id } });
|
||||
await sendWelcomeEmail(user.email); // external call
|
||||
await tx.user.update({ where: { id }, data: { emailSent: true } });
|
||||
});
|
||||
|
||||
// GOOD: external calls outside the transaction
|
||||
const user = await prisma.user.findUniqueOrThrow({ where: { id } });
|
||||
await sendWelcomeEmail(user.email);
|
||||
await prisma.user.update({ where: { id }, data: { emailSent: true } });
|
||||
|
||||
// Only raise timeout when bulk processing genuinely needs it
|
||||
await prisma.$transaction(async (tx) => { ... }, { timeout: 30_000 });
|
||||
```
|
||||
|
||||
### `migrate dev` can reset the database
|
||||
|
||||
`migrate dev` detects schema drift and may prompt to reset the DB, dropping all data.
|
||||
|
||||
```bash
|
||||
# NEVER on shared dev, staging, or production
|
||||
npx prisma migrate dev --name add_column
|
||||
|
||||
# Safe everywhere except local solo dev
|
||||
npx prisma migrate deploy
|
||||
|
||||
# Check drift without applying
|
||||
npx prisma migrate diff \
|
||||
--from-migrations ./prisma/migrations \
|
||||
--to-schema-datamodel ./prisma/schema.prisma \
|
||||
--shadow-database-url "$SHADOW_DATABASE_URL"
|
||||
```
|
||||
|
||||
### Manually editing a migration file breaks future deploys
|
||||
|
||||
Prisma checksums every migration file. Editing after apply causes `P3006 checksum mismatch` on every environment where the original already ran. Create a new migration instead.
|
||||
|
||||
### Breaking schema changes require multi-step migration
|
||||
|
||||
Adding `NOT NULL` to an existing column or renaming a column in one migration will lock the table or drop data. Use expand-and-contract:
|
||||
|
||||
```bash
|
||||
# Step 1: create migration locally, then deploy
|
||||
npx prisma migrate dev --name add_new_column # local only
|
||||
npx prisma migrate deploy # staging / production
|
||||
```
|
||||
|
||||
```ts
|
||||
// Step 2: backfill data (run in a script or migration job, not in the shell)
|
||||
await prisma.user.updateMany({ data: { newColumn: derivedValue } });
|
||||
```
|
||||
|
||||
```bash
|
||||
# Step 3: create the NOT NULL constraint migration locally, then deploy
|
||||
npx prisma migrate dev --name make_new_column_required # local only
|
||||
npx prisma migrate deploy # staging / production
|
||||
```
|
||||
|
||||
### `@updatedAt` does not fire on `updateMany`
|
||||
|
||||
`@updatedAt` is set automatically only on `update` and `upsert`. Bulk writes leave it stale.
|
||||
|
||||
```ts
|
||||
// BAD: updatedAt stays at its old value
|
||||
await prisma.post.updateMany({ where: { authorId }, data: { published: true } });
|
||||
|
||||
// GOOD
|
||||
await prisma.post.updateMany({
|
||||
where: { authorId },
|
||||
data: { published: true, updatedAt: new Date() },
|
||||
});
|
||||
```
|
||||
|
||||
### Soft delete + `findUniqueOrThrow` leaks deleted records
|
||||
|
||||
`findUniqueOrThrow` throws `P2025` only when the row does not exist in the DB. Soft-deleted rows still exist and are returned without error.
|
||||
|
||||
`findUniqueOrThrow` requires a unique constraint field in `where` — adding `deletedAt: null` alongside `id` breaks the type because `{ id, deletedAt }` is not a compound unique constraint. Use `findFirstOrThrow` instead.
|
||||
|
||||
```ts
|
||||
// BAD: returns soft-deleted user
|
||||
const user = await prisma.user.findUniqueOrThrow({ where: { id } });
|
||||
|
||||
// BAD: Prisma type error — { id, deletedAt } is not a unique constraint
|
||||
const user = await prisma.user.findUniqueOrThrow({ where: { id, deletedAt: null } });
|
||||
|
||||
// GOOD: findFirstOrThrow supports arbitrary where conditions
|
||||
const user = await prisma.user.findFirstOrThrow({ where: { id, deletedAt: null } });
|
||||
```
|
||||
|
||||
### `deleteMany` without `where` deletes every row
|
||||
|
||||
```ts
|
||||
// BAD: silently wipes the table
|
||||
await prisma.post.deleteMany();
|
||||
|
||||
// GOOD
|
||||
await prisma.post.deleteMany({ where: { authorId: userId } });
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
| Rule | Reason |
|
||||
|---|---|
|
||||
| `migrate deploy` in CI/CD, `migrate dev` only locally | `migrate dev` can reset the DB on drift |
|
||||
| Map entities to response DTOs | Prevents leaking internal fields |
|
||||
| Catch `PrismaClientKnownRequestError` at service boundary | Translate to domain errors |
|
||||
| Prefer `*OrThrow` methods over manual null checks | Throws P2025 automatically; use `findFirstOrThrow` when filtering non-unique fields |
|
||||
| `connection_limit=1` + external pooler in serverless | Prevents connection exhaustion |
|
||||
| Always provide `where` on `deleteMany` | Prevents accidental table wipe |
|
||||
| Set `updatedAt: new Date()` manually in `updateMany` | `@updatedAt` skips bulk writes |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `nestjs-patterns` — NestJS service layer that integrates Prisma
|
||||
- `postgres-patterns` — PostgreSQL-level indexing and connection tuning
|
||||
- `database-migrations` — multi-step migration planning for production
|
||||
- `backend-patterns` — general API and service layer design
|
||||
Reference in New Issue
Block a user