@dewtech/dare-cli 2.16.0 → 3.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/README.md +196 -3
- package/dist/__tests__/confidence.test.d.ts +2 -0
- package/dist/__tests__/confidence.test.d.ts.map +1 -0
- package/dist/__tests__/confidence.test.js +73 -0
- package/dist/__tests__/confidence.test.js.map +1 -0
- package/dist/__tests__/datamodel.test.d.ts +2 -0
- package/dist/__tests__/datamodel.test.d.ts.map +1 -0
- package/dist/__tests__/datamodel.test.js +131 -0
- package/dist/__tests__/datamodel.test.js.map +1 -0
- package/dist/__tests__/dna-detector.test.d.ts +2 -0
- package/dist/__tests__/dna-detector.test.d.ts.map +1 -0
- package/dist/__tests__/dna-detector.test.js +97 -0
- package/dist/__tests__/dna-detector.test.js.map +1 -0
- package/dist/__tests__/dna-facts.test.d.ts +2 -0
- package/dist/__tests__/dna-facts.test.d.ts.map +1 -0
- package/dist/__tests__/dna-facts.test.js +44 -0
- package/dist/__tests__/dna-facts.test.js.map +1 -0
- package/dist/__tests__/graph-renderer.test.d.ts +2 -0
- package/dist/__tests__/graph-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/graph-renderer.test.js +85 -0
- package/dist/__tests__/graph-renderer.test.js.map +1 -0
- package/dist/__tests__/migration.test.d.ts +2 -0
- package/dist/__tests__/migration.test.d.ts.map +1 -0
- package/dist/__tests__/migration.test.js +77 -0
- package/dist/__tests__/migration.test.js.map +1 -0
- package/dist/__tests__/module-detector.test.d.ts +2 -0
- package/dist/__tests__/module-detector.test.d.ts.map +1 -0
- package/dist/__tests__/module-detector.test.js +83 -0
- package/dist/__tests__/module-detector.test.js.map +1 -0
- package/dist/__tests__/refine.test.d.ts +2 -0
- package/dist/__tests__/refine.test.d.ts.map +1 -0
- package/dist/__tests__/refine.test.js +186 -0
- package/dist/__tests__/refine.test.js.map +1 -0
- package/dist/__tests__/reverse-facts.test.d.ts +2 -0
- package/dist/__tests__/reverse-facts.test.d.ts.map +1 -0
- package/dist/__tests__/reverse-facts.test.js +78 -0
- package/dist/__tests__/reverse-facts.test.js.map +1 -0
- package/dist/__tests__/review.test.d.ts +2 -0
- package/dist/__tests__/review.test.d.ts.map +1 -0
- package/dist/__tests__/review.test.js +242 -0
- package/dist/__tests__/review.test.js.map +1 -0
- package/dist/__tests__/update.test.d.ts +2 -0
- package/dist/__tests__/update.test.d.ts.map +1 -0
- package/dist/__tests__/update.test.js +150 -0
- package/dist/__tests__/update.test.js.map +1 -0
- package/dist/__tests__/validate.test.js +65 -65
- package/dist/bin/dare.js +38 -3
- package/dist/bin/dare.js.map +1 -1
- package/dist/commands/blueprint.js +122 -122
- package/dist/commands/dag.d.ts.map +1 -1
- package/dist/commands/dag.js +43 -79
- package/dist/commands/dag.js.map +1 -1
- package/dist/commands/dna.d.ts +3 -0
- package/dist/commands/dna.d.ts.map +1 -0
- package/dist/commands/dna.js +69 -0
- package/dist/commands/dna.js.map +1 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +76 -0
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +101 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/new.d.ts +16 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +103 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/refine.d.ts +16 -0
- package/dist/commands/refine.d.ts.map +1 -0
- package/dist/commands/refine.js +167 -0
- package/dist/commands/refine.js.map +1 -0
- package/dist/commands/reverse.d.ts +3 -0
- package/dist/commands/reverse.d.ts.map +1 -0
- package/dist/commands/reverse.js +201 -0
- package/dist/commands/reverse.js.map +1 -0
- package/dist/commands/review.d.ts +16 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +106 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/update.d.ts +13 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +149 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/welcome.d.ts +14 -0
- package/dist/commands/welcome.d.ts.map +1 -0
- package/dist/commands/welcome.js +29 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/skills/commands/add.d.ts +23 -0
- package/dist/skills/commands/add.d.ts.map +1 -0
- package/dist/skills/commands/add.js +206 -0
- package/dist/skills/commands/add.js.map +1 -0
- package/dist/skills/commands/info.d.ts +14 -0
- package/dist/skills/commands/info.d.ts.map +1 -0
- package/dist/skills/commands/info.js +99 -0
- package/dist/skills/commands/info.js.map +1 -0
- package/dist/skills/commands/list.d.ts +19 -0
- package/dist/skills/commands/list.d.ts.map +1 -0
- package/dist/skills/commands/list.js +163 -0
- package/dist/skills/commands/list.js.map +1 -0
- package/dist/skills/commands/publish.d.ts +56 -0
- package/dist/skills/commands/publish.d.ts.map +1 -0
- package/dist/skills/commands/publish.js +272 -0
- package/dist/skills/commands/publish.js.map +1 -0
- package/dist/skills/commands/remove.d.ts +19 -0
- package/dist/skills/commands/remove.d.ts.map +1 -0
- package/dist/skills/commands/remove.js +96 -0
- package/dist/skills/commands/remove.js.map +1 -0
- package/dist/skills/commands/update.d.ts +31 -0
- package/dist/skills/commands/update.d.ts.map +1 -0
- package/dist/skills/commands/update.js +132 -0
- package/dist/skills/commands/update.js.map +1 -0
- package/dist/skills/index.d.ts +22 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +33 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/manifest.d.ts +54 -0
- package/dist/skills/manifest.d.ts.map +1 -0
- package/dist/skills/manifest.js +162 -0
- package/dist/skills/manifest.js.map +1 -0
- package/dist/skills/registry-local.d.ts +67 -0
- package/dist/skills/registry-local.d.ts.map +1 -0
- package/dist/skills/registry-local.js +130 -0
- package/dist/skills/registry-local.js.map +1 -0
- package/dist/skills/registry-mock.json +109 -0
- package/dist/skills/registry-remote.d.ts +110 -0
- package/dist/skills/registry-remote.d.ts.map +1 -0
- package/dist/skills/registry-remote.js +246 -0
- package/dist/skills/registry-remote.js.map +1 -0
- package/dist/skills/registry.d.ts +49 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +94 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/tests/manifest.spec.d.ts +8 -0
- package/dist/skills/tests/manifest.spec.d.ts.map +1 -0
- package/dist/skills/tests/manifest.spec.js +176 -0
- package/dist/skills/tests/manifest.spec.js.map +1 -0
- package/dist/skills/tests/publish.spec.d.ts +12 -0
- package/dist/skills/tests/publish.spec.d.ts.map +1 -0
- package/dist/skills/tests/publish.spec.js +276 -0
- package/dist/skills/tests/publish.spec.js.map +1 -0
- package/dist/skills/tests/registry-local.spec.d.ts +8 -0
- package/dist/skills/tests/registry-local.spec.d.ts.map +1 -0
- package/dist/skills/tests/registry-local.spec.js +231 -0
- package/dist/skills/tests/registry-local.spec.js.map +1 -0
- package/dist/skills/tests/registry.spec.d.ts +7 -0
- package/dist/skills/tests/registry.spec.d.ts.map +1 -0
- package/dist/skills/tests/registry.spec.js +58 -0
- package/dist/skills/tests/registry.spec.js.map +1 -0
- package/dist/skills/tests/remote-registry.spec.d.ts +9 -0
- package/dist/skills/tests/remote-registry.spec.d.ts.map +1 -0
- package/dist/skills/tests/remote-registry.spec.js +357 -0
- package/dist/skills/tests/remote-registry.spec.js.map +1 -0
- package/dist/skills/tests/update.spec.d.ts +9 -0
- package/dist/skills/tests/update.spec.d.ts.map +1 -0
- package/dist/skills/tests/update.spec.js +166 -0
- package/dist/skills/tests/update.spec.js.map +1 -0
- package/dist/types/Refine.types.d.ts +96 -0
- package/dist/types/Refine.types.d.ts.map +1 -0
- package/dist/types/Refine.types.js +19 -0
- package/dist/types/Refine.types.js.map +1 -0
- package/dist/types/Review.types.d.ts +86 -0
- package/dist/types/Review.types.d.ts.map +1 -0
- package/dist/types/Review.types.js +15 -0
- package/dist/types/Review.types.js.map +1 -0
- package/dist/types/UpdateManifest.types.d.ts +91 -0
- package/dist/types/UpdateManifest.types.d.ts.map +1 -0
- package/dist/types/UpdateManifest.types.js +13 -0
- package/dist/types/UpdateManifest.types.js.map +1 -0
- package/dist/utils/ReviewRunner.d.ts +42 -0
- package/dist/utils/ReviewRunner.d.ts.map +1 -0
- package/dist/utils/ReviewRunner.js +175 -0
- package/dist/utils/ReviewRunner.js.map +1 -0
- package/dist/utils/UpdateApplier.d.ts +42 -0
- package/dist/utils/UpdateApplier.d.ts.map +1 -0
- package/dist/utils/UpdateApplier.js +207 -0
- package/dist/utils/UpdateApplier.js.map +1 -0
- package/dist/utils/UpdateDetector.d.ts +56 -0
- package/dist/utils/UpdateDetector.d.ts.map +1 -0
- package/dist/utils/UpdateDetector.js +164 -0
- package/dist/utils/UpdateDetector.js.map +1 -0
- package/dist/utils/banner.d.ts +28 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +77 -0
- package/dist/utils/banner.js.map +1 -0
- package/dist/utils/banner.spec.d.ts +5 -0
- package/dist/utils/banner.spec.d.ts.map +1 -0
- package/dist/utils/banner.spec.js +253 -0
- package/dist/utils/banner.spec.js.map +1 -0
- package/dist/utils/complexity-analyzer.d.ts +60 -0
- package/dist/utils/complexity-analyzer.d.ts.map +1 -0
- package/dist/utils/complexity-analyzer.js +292 -0
- package/dist/utils/complexity-analyzer.js.map +1 -0
- package/dist/utils/confidence.d.ts +41 -0
- package/dist/utils/confidence.d.ts.map +1 -0
- package/dist/utils/confidence.js +101 -0
- package/dist/utils/confidence.js.map +1 -0
- package/dist/utils/datamodel.d.ts +41 -0
- package/dist/utils/datamodel.d.ts.map +1 -0
- package/dist/utils/datamodel.js +535 -0
- package/dist/utils/datamodel.js.map +1 -0
- package/dist/utils/dna-detector.d.ts +61 -0
- package/dist/utils/dna-detector.d.ts.map +1 -0
- package/dist/utils/dna-detector.js +354 -0
- package/dist/utils/dna-detector.js.map +1 -0
- package/dist/utils/dna-facts.d.ts +13 -0
- package/dist/utils/dna-facts.d.ts.map +1 -0
- package/dist/utils/dna-facts.js +109 -0
- package/dist/utils/dna-facts.js.map +1 -0
- package/dist/utils/excalidraw-renderer.d.ts +11 -71
- package/dist/utils/excalidraw-renderer.d.ts.map +1 -1
- package/dist/utils/excalidraw-renderer.js +29 -162
- package/dist/utils/excalidraw-renderer.js.map +1 -1
- package/dist/utils/graph-renderer.d.ts +115 -0
- package/dist/utils/graph-renderer.d.ts.map +1 -0
- package/dist/utils/graph-renderer.js +216 -0
- package/dist/utils/graph-renderer.js.map +1 -0
- package/dist/utils/migration.d.ts +64 -0
- package/dist/utils/migration.d.ts.map +1 -0
- package/dist/utils/migration.js +183 -0
- package/dist/utils/migration.js.map +1 -0
- package/dist/utils/module-detector.d.ts +46 -0
- package/dist/utils/module-detector.d.ts.map +1 -0
- package/dist/utils/module-detector.js +348 -0
- package/dist/utils/module-detector.js.map +1 -0
- package/dist/utils/project-generator.d.ts.map +1 -1
- package/dist/utils/project-generator.js +273 -254
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/reverse-facts.d.ts +50 -0
- package/dist/utils/reverse-facts.d.ts.map +1 -0
- package/dist/utils/reverse-facts.js +291 -0
- package/dist/utils/reverse-facts.js.map +1 -0
- package/dist/utils/stack-bootstrap.js +371 -371
- package/dist/utils/static-analyzer.d.ts +29 -0
- package/dist/utils/static-analyzer.d.ts.map +1 -0
- package/dist/utils/static-analyzer.js +390 -0
- package/dist/utils/static-analyzer.js.map +1 -0
- package/dist/utils/version-compare.d.ts +27 -0
- package/dist/utils/version-compare.d.ts.map +1 -0
- package/dist/utils/version-compare.js +47 -0
- package/dist/utils/version-compare.js.map +1 -0
- package/package.json +8 -3
- package/templates/DARE-dag-example.yaml +280 -280
- package/templates/UPDATE-MANIFEST.json +48 -0
- package/templates/backend/node-nestjs/.env.example +9 -9
- package/templates/backend/node-nestjs/nest-cli.json +8 -8
- package/templates/backend/node-nestjs/package.json +50 -50
- package/templates/backend/node-nestjs/src/app.controller.ts +12 -12
- package/templates/backend/node-nestjs/src/app.module.ts +15 -15
- package/templates/backend/node-nestjs/src/app.service.ts +8 -8
- package/templates/backend/node-nestjs/src/main.ts +24 -24
- package/templates/backend/node-nestjs/tsconfig.json +21 -21
- package/templates/backend/php-laravel/.env.example +22 -22
- package/templates/backend/php-laravel/app/Http/Controllers/HealthController.php +15 -15
- package/templates/backend/php-laravel/composer.json +40 -40
- package/templates/backend/python-fastapi/.env.example +4 -4
- package/templates/backend/python-fastapi/app/api/router.py +8 -8
- package/templates/backend/python-fastapi/app/core/config.py +20 -20
- package/templates/backend/python-fastapi/main.py +35 -35
- package/templates/backend/python-fastapi/requirements.txt +13 -13
- package/templates/backend/rust-axum/.env.example +3 -3
- package/templates/backend/rust-axum/Cargo.toml +23 -23
- package/templates/backend/rust-axum/src/errors.rs +30 -30
- package/templates/backend/rust-axum/src/main.rs +32 -32
- package/templates/backend/rust-axum/src/routes.rs +6 -6
- package/templates/frontend/leptos-csr/.cargo/config.toml +2 -2
- package/templates/frontend/leptos-csr/Cargo.toml +16 -16
- package/templates/frontend/leptos-csr/Trunk.toml +10 -10
- package/templates/frontend/leptos-csr/index.html +11 -11
- package/templates/frontend/leptos-csr/src/lib.rs +20 -20
- package/templates/frontend/leptos-csr/style/main.scss +19 -19
- package/templates/frontend/leptos-fullstack/.cargo/config.toml +4 -4
- package/templates/frontend/leptos-fullstack/Cargo.toml +56 -56
- package/templates/frontend/leptos-fullstack/src/app.rs +49 -49
- package/templates/frontend/leptos-fullstack/src/lib.rs +9 -9
- package/templates/frontend/leptos-fullstack/src/main.rs +29 -29
- package/templates/frontend/leptos-fullstack/style/main.scss +19 -19
- package/templates/frontend/react/index.html +12 -12
- package/templates/frontend/react/package.json +35 -35
- package/templates/frontend/react/src/App.tsx +25 -25
- package/templates/frontend/react/src/main.tsx +9 -9
- package/templates/frontend/vue/package.json +32 -32
- package/templates/frontend/vue/src/App.vue +7 -7
- package/templates/frontend/vue/src/main.ts +10 -10
- package/templates/frontend/vue/src/router/index.ts +14 -14
- package/templates/frontend/vue/src/views/HomeView.vue +6 -6
- package/templates/hooks/pre-commit-dare-validate +24 -24
- package/templates/ide/antigravity/.agents/skills/dare-ax/SKILL.md +152 -0
- package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +180 -36
- package/templates/ide/antigravity/.agents/skills/dare-dag-build/SKILL.md +154 -0
- package/templates/ide/antigravity/.agents/skills/dare-dag-run/SKILL.md +130 -0
- package/templates/ide/antigravity/.agents/skills/dare-dag-runner/SKILL.md +203 -203
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +63 -0
- package/templates/ide/antigravity/.agents/skills/dare-docker/SKILL.md +315 -0
- package/templates/ide/antigravity/.agents/skills/dare-frontend-design/SKILL.md +192 -0
- package/templates/ide/antigravity/.agents/skills/dare-laravel-api/SKILL.md +337 -0
- package/templates/ide/antigravity/.agents/skills/dare-layered-design/SKILL.md +166 -0
- package/templates/ide/antigravity/.agents/skills/dare-llm-integration/SKILL.md +217 -0
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +61 -0
- package/templates/ide/antigravity/.agents/skills/dare-quality-telemetry/SKILL.md +187 -0
- package/templates/ide/antigravity/.agents/skills/dare-realtime/SKILL.md +217 -0
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +114 -0
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +108 -0
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +111 -0
- package/templates/ide/antigravity/.agents/skills/dare-rust-leptos/SKILL.md +263 -0
- package/templates/ide/antigravity/.agents/skills/dare-rust-workspace/SKILL.md +275 -275
- package/templates/ide/antigravity/.agents/skills/dare-security/SKILL.md +274 -0
- package/templates/ide/antigravity/.agents/skills/dare-tasks/SKILL.md +265 -224
- package/templates/ide/antigravity/.agents/skills/dare-telemetry/SKILL.md +188 -0
- package/templates/ide/antigravity/.agents/skills/skill-fastapi-api/SKILL.md +343 -0
- package/templates/ide/antigravity/.agents/skills/skill-go-gin-api/SKILL.md +377 -0
- package/templates/ide/antigravity/.agents/skills/skill-mcp-server/SKILL.md +382 -0
- package/templates/ide/antigravity/.agents/skills/skill-nestjs-api/SKILL.md +326 -0
- package/templates/ide/antigravity/.agents/skills/skill-rails-api/SKILL.md +393 -0
- package/templates/ide/antigravity/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/antigravity/templates/DESIGN-template.md +129 -129
- package/templates/ide/antigravity/templates/TASK-SPEC-template.md +141 -100
- package/templates/ide/claude/.claude/commands/dare-ax.md +131 -0
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +134 -78
- package/templates/ide/claude/.claude/commands/dare-bugfix-design.md +119 -0
- package/templates/ide/claude/.claude/commands/dare-dag-build.md +151 -110
- package/templates/ide/claude/.claude/commands/dare-dag-run.md +109 -109
- package/templates/ide/claude/.claude/commands/dare-dag-runner.md +117 -0
- package/templates/ide/claude/.claude/commands/dare-dag-viz.md +197 -197
- package/templates/ide/claude/.claude/commands/dare-design.md +69 -69
- package/templates/ide/claude/.claude/commands/dare-dna.md +75 -0
- package/templates/ide/claude/.claude/commands/dare-docker.md +207 -0
- package/templates/ide/claude/.claude/commands/dare-execute.md +152 -152
- package/templates/ide/claude/.claude/commands/dare-feature-design.md +147 -0
- package/templates/ide/claude/.claude/commands/dare-frontend-design.md +149 -0
- package/templates/ide/claude/.claude/commands/dare-laravel-api.md +211 -0
- package/templates/ide/claude/.claude/commands/dare-layered-design.md +124 -0
- package/templates/ide/claude/.claude/commands/dare-llm-integration.md +148 -0
- package/templates/ide/claude/.claude/commands/dare-migrate.md +72 -0
- package/templates/ide/claude/.claude/commands/dare-quality-telemetry.md +166 -0
- package/templates/ide/claude/.claude/commands/dare-realtime.md +159 -0
- package/templates/ide/claude/.claude/commands/dare-refine.md +145 -0
- package/templates/ide/claude/.claude/commands/dare-reverse.md +139 -0
- package/templates/ide/claude/.claude/commands/dare-review.md +113 -0
- package/templates/ide/claude/.claude/commands/dare-rust-leptos.md +269 -269
- package/templates/ide/claude/.claude/commands/dare-rust-workspace.md +209 -209
- package/templates/ide/claude/.claude/commands/dare-security.md +232 -232
- package/templates/ide/claude/.claude/commands/dare-tasks.md +70 -70
- package/templates/ide/claude/.claude/commands/dare-telemetry.md +132 -0
- package/templates/ide/claude/.claude/commands/skill-fastapi-api.md +205 -0
- package/templates/ide/claude/.claude/commands/skill-go-gin-api.md +232 -0
- package/templates/ide/claude/.claude/commands/skill-mcp-server.md +228 -0
- package/templates/ide/claude/.claude/commands/skill-nestjs-api.md +210 -0
- package/templates/ide/claude/.claude/commands/skill-rails-api.md +236 -0
- package/templates/ide/claude/.claude/settings.example.json +35 -35
- package/templates/ide/claude/CLAUDE.md +146 -146
- package/templates/ide/claude/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/claude/templates/DESIGN-template.md +129 -129
- package/templates/ide/claude/templates/TASK-SPEC-template.md +141 -100
- package/templates/ide/cursor/.cursor/commands/dag-viz.md +139 -0
- package/templates/ide/cursor/.cursor/commands/generate-blueprint.md +86 -41
- package/templates/ide/cursor/.cursor/commands/generate-design.md +35 -35
- package/templates/ide/cursor/.cursor/commands/generate-tasks.md +184 -142
- package/templates/ide/cursor/.cursor/commands/refine-task.md +107 -0
- package/templates/ide/cursor/.cursor/commands/review-task.md +91 -0
- package/templates/ide/cursor/.cursor/commands/run-dag.md +110 -110
- package/templates/ide/cursor/.cursor/rules/skill-ax.mdc +263 -0
- package/templates/ide/cursor/.cursor/rules/skill-dag-build.mdc +173 -0
- package/templates/ide/cursor/.cursor/rules/skill-dag-run.mdc +134 -0
- package/templates/ide/cursor/.cursor/rules/skill-dag-runner.mdc +221 -221
- package/templates/ide/cursor/.cursor/rules/skill-dna.mdc +63 -0
- package/templates/ide/cursor/.cursor/rules/skill-fastapi-api.mdc +352 -0
- package/templates/ide/cursor/.cursor/rules/skill-frontend-design.mdc +244 -0
- package/templates/ide/cursor/.cursor/rules/skill-go-gin-api.mdc +371 -0
- package/templates/ide/cursor/.cursor/rules/skill-layered-design.mdc +266 -0
- package/templates/ide/cursor/.cursor/rules/skill-llm-integration.mdc +295 -0
- package/templates/ide/cursor/.cursor/rules/skill-mcp-server.mdc +367 -0
- package/templates/ide/cursor/.cursor/rules/skill-migrate.mdc +58 -0
- package/templates/ide/cursor/.cursor/rules/skill-nestjs-api.mdc +346 -0
- package/templates/ide/cursor/.cursor/rules/skill-quality-telemetry.mdc +248 -0
- package/templates/ide/cursor/.cursor/rules/skill-rails-api.mdc +400 -0
- package/templates/ide/cursor/.cursor/rules/skill-realtime.mdc +262 -0
- package/templates/ide/cursor/.cursor/rules/skill-reverse.mdc +107 -0
- package/templates/ide/cursor/.cursor/rules/skill-rust-leptos.mdc +281 -0
- package/templates/ide/cursor/.cursor/rules/skill-rust-workspace.mdc +312 -312
- package/templates/ide/cursor/.cursor/rules/skill-security.mdc +245 -245
- package/templates/ide/cursor/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/cursor/templates/DESIGN-template.md +129 -129
- package/templates/ide/cursor/templates/TASK-SPEC-template.md +141 -100
- package/templates/shared/docker-compose.yml +41 -41
- package/dist/__tests__/dag-runner/adapters.test.d.ts +0 -2
- package/dist/__tests__/dag-runner/adapters.test.d.ts.map +0 -1
- package/dist/__tests__/dag-runner/adapters.test.js +0 -134
- package/dist/__tests__/dag-runner/adapters.test.js.map +0 -1
- package/dist/dag-runner/adapters/antigravity.d.ts +0 -6
- package/dist/dag-runner/adapters/antigravity.d.ts.map +0 -1
- package/dist/dag-runner/adapters/antigravity.js +0 -54
- package/dist/dag-runner/adapters/antigravity.js.map +0 -1
- package/dist/dag-runner/adapters/claude.d.ts +0 -6
- package/dist/dag-runner/adapters/claude.d.ts.map +0 -1
- package/dist/dag-runner/adapters/claude.js +0 -48
- package/dist/dag-runner/adapters/claude.js.map +0 -1
- package/dist/dag-runner/adapters/cursor.d.ts +0 -6
- package/dist/dag-runner/adapters/cursor.d.ts.map +0 -1
- package/dist/dag-runner/adapters/cursor.js +0 -58
- package/dist/dag-runner/adapters/cursor.js.map +0 -1
- package/dist/dag-runner/adapters/index.d.ts +0 -46
- package/dist/dag-runner/adapters/index.d.ts.map +0 -1
- package/dist/dag-runner/adapters/index.js +0 -55
- package/dist/dag-runner/adapters/index.js.map +0 -1
- package/dist/dag-runner/utils/timeout.d.ts +0 -27
- package/dist/dag-runner/utils/timeout.d.ts.map +0 -1
- package/dist/dag-runner/utils/timeout.js +0 -55
- package/dist/dag-runner/utils/timeout.js.map +0 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# /skill-mcp-server
|
|
2
|
+
|
|
3
|
+
Padrões DARE para servidores MCP (Model Context Protocol) em TypeScript ou Python. Tools, resources, prompts; transports stdio/SSE/HTTP; validação Zod/Pydantic; autorização por tool; tracing; testes; publicação.
|
|
4
|
+
|
|
5
|
+
## Como usar
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/skill-mcp-server # audita server MCP atual
|
|
9
|
+
/skill-mcp-server scaffold ts # scaffold TypeScript stdio
|
|
10
|
+
/skill-mcp-server scaffold py # scaffold Python stdio
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## O que é MCP
|
|
14
|
+
|
|
15
|
+
Protocolo aberto da Anthropic para conectar agentes a fontes de dados e ações. Server expõe:
|
|
16
|
+
- **Tools** — funções chamáveis (ex: `create_issue`)
|
|
17
|
+
- **Resources** — dados leitáveis (ex: schemas)
|
|
18
|
+
- **Prompts** — templates parametrizados
|
|
19
|
+
|
|
20
|
+
Transports: **stdio** (local), **SSE** (remoto streaming), **HTTP** (remoto stateless).
|
|
21
|
+
|
|
22
|
+
## Stack (TypeScript)
|
|
23
|
+
|
|
24
|
+
- Node 20+ + TS 5.5+
|
|
25
|
+
- `@modelcontextprotocol/sdk`
|
|
26
|
+
- Zod para schemas
|
|
27
|
+
- vitest
|
|
28
|
+
|
|
29
|
+
## Stack (Python)
|
|
30
|
+
|
|
31
|
+
- Python 3.11+
|
|
32
|
+
- `mcp` (SDK oficial)
|
|
33
|
+
- Pydantic v2
|
|
34
|
+
- pytest + pytest-asyncio
|
|
35
|
+
|
|
36
|
+
## Estrutura
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
mcp-server/
|
|
40
|
+
├── src/
|
|
41
|
+
│ ├── index.ts ← stdio entrypoint
|
|
42
|
+
│ ├── server.ts
|
|
43
|
+
│ ├── tools/
|
|
44
|
+
│ ├── resources/
|
|
45
|
+
│ ├── prompts/
|
|
46
|
+
│ └── schemas/
|
|
47
|
+
└── tests/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Exemplo (TypeScript stdio)
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
54
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
55
|
+
import { z } from 'zod';
|
|
56
|
+
|
|
57
|
+
const CreateIssueInput = z.object({
|
|
58
|
+
title: z.string().min(1).max(255),
|
|
59
|
+
description: z.string().optional(),
|
|
60
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
61
|
+
team_id: z.string().uuid(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const server = new Server(
|
|
65
|
+
{ name: 'meu-server', version: '1.0.0' },
|
|
66
|
+
{ capabilities: { tools: {} } }
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
70
|
+
tools: [{
|
|
71
|
+
name: 'create_issue',
|
|
72
|
+
description: 'Create a new issue in Linear',
|
|
73
|
+
inputSchema: CreateIssueInput.shape,
|
|
74
|
+
}],
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
78
|
+
const input = CreateIssueInput.parse(req.params.arguments);
|
|
79
|
+
const result = await linearClient.createIssue(input);
|
|
80
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const transport = new StdioServerTransport();
|
|
84
|
+
await server.connect(transport);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Exemplo (Python stdio)
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from mcp.server import Server
|
|
91
|
+
from mcp.server.stdio import stdio_server
|
|
92
|
+
from mcp.types import Tool, TextContent
|
|
93
|
+
from pydantic import BaseModel, Field
|
|
94
|
+
import asyncio
|
|
95
|
+
|
|
96
|
+
app = Server("meu-server")
|
|
97
|
+
|
|
98
|
+
class CreateIssueInput(BaseModel):
|
|
99
|
+
title: str = Field(min_length=1, max_length=255)
|
|
100
|
+
description: str | None = None
|
|
101
|
+
priority: str = Field(default="medium", pattern="^(low|medium|high)$")
|
|
102
|
+
team_id: str
|
|
103
|
+
|
|
104
|
+
@app.list_tools()
|
|
105
|
+
async def list_tools():
|
|
106
|
+
return [Tool(
|
|
107
|
+
name="create_issue",
|
|
108
|
+
description="Create a new issue",
|
|
109
|
+
inputSchema=CreateIssueInput.model_json_schema(),
|
|
110
|
+
)]
|
|
111
|
+
|
|
112
|
+
@app.call_tool()
|
|
113
|
+
async def call_tool(name: str, arguments: dict):
|
|
114
|
+
input_data = CreateIssueInput.model_validate(arguments)
|
|
115
|
+
result = await linear_client.create_issue(input_data)
|
|
116
|
+
return [TextContent(type="text", text=result.model_dump_json())]
|
|
117
|
+
|
|
118
|
+
async def main():
|
|
119
|
+
async with stdio_server() as (read, write):
|
|
120
|
+
await app.run(read, write, app.create_initialization_options())
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
asyncio.run(main())
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Resources e Prompts
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
130
|
+
resources: [{ uri: 'linear://schema', name: 'Schema', mimeType: 'application/json' }],
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
134
|
+
prompts: [{
|
|
135
|
+
name: 'triage_issue',
|
|
136
|
+
description: 'Triage and assign priority',
|
|
137
|
+
arguments: [{ name: 'description', required: true }],
|
|
138
|
+
}],
|
|
139
|
+
}));
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Autorização por tool
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
if (input.confirm !== 'YES_I_AM_SURE') throw new Error('confirm required');
|
|
146
|
+
if (!callerHasRole('admin')) throw new Error('admin only');
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Observabilidade
|
|
150
|
+
|
|
151
|
+
**stdio: logs em stderr SEMPRE** (stdout é o protocolo).
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
console.error(JSON.stringify({
|
|
155
|
+
ts: new Date().toISOString(),
|
|
156
|
+
tool: req.params.name,
|
|
157
|
+
duration_ms: Date.now() - start,
|
|
158
|
+
success: true,
|
|
159
|
+
}));
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Antipatterns
|
|
163
|
+
|
|
164
|
+
| AP | Antipattern | Por quê |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| AP-01 | Logs no stdout (stdio) | Quebra o protocolo |
|
|
167
|
+
| AP-02 | Tool sem schema | Cliente passa lixo |
|
|
168
|
+
| AP-03 | Sem validação no server | Confia no client (errado) |
|
|
169
|
+
| AP-04 | Tool destrutiva sem confirm | Agente apaga sem querer |
|
|
170
|
+
| AP-05 | Secrets em código | Use env vars |
|
|
171
|
+
| AP-06 | Tool name vago (`do_thing`) | Agente não sabe quando usar |
|
|
172
|
+
| AP-07 | Description vago | Erra na escolha |
|
|
173
|
+
| AP-08 | Resource sem mimeType | Cliente não parseia |
|
|
174
|
+
|
|
175
|
+
## Testes
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Unit
|
|
179
|
+
test('valida input', () => {
|
|
180
|
+
const valid = CreateIssueInput.parse({ title: 'Test', team_id: '550e8400-e29b-41d4-a716-446655440000' });
|
|
181
|
+
expect(valid.priority).toBe('medium');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// E2E com client MCP
|
|
185
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
186
|
+
const client = new Client({ name: 'test', version: '0.0.0' }, { capabilities: {} });
|
|
187
|
+
await client.connect(transport);
|
|
188
|
+
const tools = await client.listTools();
|
|
189
|
+
expect(tools.tools.map(t => t.name)).toContain('create_issue');
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Configuração no Claude Desktop
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"meu-server": {
|
|
198
|
+
"command": "node",
|
|
199
|
+
"args": ["/abs/path/dist/index.js"],
|
|
200
|
+
"env": { "LINEAR_API_KEY": "lin_api_..." }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Publicação
|
|
207
|
+
|
|
208
|
+
- README com tools, env vars, exemplo de config
|
|
209
|
+
- LICENSE MIT
|
|
210
|
+
- `package.json` ou `pyproject.toml` versionado
|
|
211
|
+
- npm publish (TS) ou PyPI (Python)
|
|
212
|
+
- Para SSE/HTTP: Docker multi-stage
|
|
213
|
+
|
|
214
|
+
## O que fazer
|
|
215
|
+
|
|
216
|
+
1. Decidir transport (stdio para local; SSE/HTTP para remoto)
|
|
217
|
+
2. Listar tools/resources/prompts no DESIGN.md
|
|
218
|
+
3. Scaffold com SDK oficial
|
|
219
|
+
4. Schema Zod/Pydantic para cada tool
|
|
220
|
+
5. Logs em stderr, JSON estruturado
|
|
221
|
+
6. Testes vitest/pytest + E2E com client MCP
|
|
222
|
+
7. README + LICENSE MIT + publicação
|
|
223
|
+
|
|
224
|
+
$ARGUMENTS
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
Skill MIT — parte do DARE Method.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# /skill-nestjs-api
|
|
2
|
+
|
|
3
|
+
Padrões DARE para APIs REST em NestJS + TypeScript + Prisma + Swagger. Modules, Controllers, Services, DTOs com class-validator, Guards JWT, throttler, exceções globais, Jest + Supertest, OpenAPI auto-gerado.
|
|
4
|
+
|
|
5
|
+
## Como usar
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/skill-nestjs-api # audita projeto NestJS
|
|
9
|
+
/skill-nestjs-api scaffold users # gera CRUD com módulo + camadas
|
|
10
|
+
/skill-nestjs-api migrate-validation # extrai @Body any para DTOs com class-validator
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Stack canônica
|
|
14
|
+
|
|
15
|
+
- TypeScript 5.5+ com `strict: true`
|
|
16
|
+
- NestJS 11.x
|
|
17
|
+
- Prisma 5.x (PostgreSQL)
|
|
18
|
+
- class-validator + class-transformer
|
|
19
|
+
- @nestjs/swagger (OpenAPI auto)
|
|
20
|
+
- @nestjs/throttler (rate limit)
|
|
21
|
+
- @nestjs/passport + @nestjs/jwt
|
|
22
|
+
- Jest + Supertest
|
|
23
|
+
|
|
24
|
+
## Estrutura por módulo
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
src/<feature>/
|
|
28
|
+
├── <feature>.module.ts
|
|
29
|
+
├── <feature>.controller.ts ← Handler
|
|
30
|
+
├── <feature>.service.ts ← Service
|
|
31
|
+
├── <feature>.repository.ts ← Repository (Prisma)
|
|
32
|
+
├── dto/
|
|
33
|
+
├── entities/
|
|
34
|
+
└── tests/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Controllers
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
@ApiTags('users')
|
|
41
|
+
@Controller('users')
|
|
42
|
+
@UseGuards(JwtAuthGuard)
|
|
43
|
+
export class UsersController {
|
|
44
|
+
constructor(private readonly register: RegisterUserService) {}
|
|
45
|
+
|
|
46
|
+
@Post()
|
|
47
|
+
@ApiOperation({ summary: 'Criar usuário' })
|
|
48
|
+
@ApiCreatedResponse({ type: UserResponseDto })
|
|
49
|
+
async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
|
|
50
|
+
const user = await this.register.execute(dto);
|
|
51
|
+
return UserResponseDto.from(user);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## DTOs (class-validator)
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
export class CreateUserDto {
|
|
60
|
+
@ApiProperty({ example: 'jane@example.com' })
|
|
61
|
+
@IsEmail() email!: string;
|
|
62
|
+
|
|
63
|
+
@ApiProperty({ minLength: 12 })
|
|
64
|
+
@IsString() @MinLength(12) password!: string;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`main.ts`:
|
|
69
|
+
```typescript
|
|
70
|
+
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }));
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Services
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
@Injectable()
|
|
77
|
+
export class RegisterUserService {
|
|
78
|
+
constructor(private readonly repo: UsersRepository) {}
|
|
79
|
+
|
|
80
|
+
async execute(dto: CreateUserDto): Promise<User> {
|
|
81
|
+
if (await this.repo.existsByEmail(dto.email)) throw new UserAlreadyExistsError();
|
|
82
|
+
return this.repo.create({ ...dto, password: await hash(dto.password) });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Repositories
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
@Injectable()
|
|
91
|
+
export class UsersRepository {
|
|
92
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
93
|
+
|
|
94
|
+
async existsByEmail(email: string): Promise<boolean> {
|
|
95
|
+
return !!(await this.prisma.user.findUnique({ where: { email } }));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## OpenAPI + Swagger
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const config = new DocumentBuilder()
|
|
104
|
+
.setTitle('Projeto API').setVersion('1.0').addBearerAuth().build();
|
|
105
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
106
|
+
SwaggerModule.setup('docs', app, document);
|
|
107
|
+
writeFileSync('./public/openapi.json', JSON.stringify(document));
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Rate limiting
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
ThrottlerModule.forRoot([{ ttl: 60_000, limit: 100 }]),
|
|
114
|
+
|
|
115
|
+
// Login: 5/15min
|
|
116
|
+
@Throttle({ default: { limit: 5, ttl: 900_000 } })
|
|
117
|
+
@Post('login')
|
|
118
|
+
async login() {...}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Filter global de exceções
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
@Catch()
|
|
125
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
126
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
127
|
+
const response = host.switchToHttp().getResponse<Response>();
|
|
128
|
+
if (exception instanceof UserAlreadyExistsError) {
|
|
129
|
+
return response.status(409).json({ error: 'USER_EXISTS' });
|
|
130
|
+
}
|
|
131
|
+
if (exception instanceof HttpException) {
|
|
132
|
+
return response.status(exception.getStatus()).json(exception.getResponse());
|
|
133
|
+
}
|
|
134
|
+
return response.status(500).json({ error: 'INTERNAL' });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Testes
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Unit
|
|
143
|
+
describe('RegisterUserService', () => {
|
|
144
|
+
it('falha se email já existe', async () => {
|
|
145
|
+
const repo = { existsByEmail: jest.fn().mockResolvedValue(true) } as any;
|
|
146
|
+
const sut = new RegisterUserService(repo);
|
|
147
|
+
await expect(sut.execute({...})).rejects.toThrow(UserAlreadyExistsError);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// E2E (Supertest)
|
|
152
|
+
it('POST /users cria com sucesso', async () => {
|
|
153
|
+
const res = await request(app.getHttpServer())
|
|
154
|
+
.post('/users')
|
|
155
|
+
.set('Authorization', `Bearer ${token}`)
|
|
156
|
+
.send({ email: 'jane@example.com', name: 'Jane', password: 'longsecret123' });
|
|
157
|
+
expect(res.status).toBe(201);
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Antipatterns
|
|
162
|
+
|
|
163
|
+
| AP | Antipattern | Correção |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| AP-01 | Validação inline | DTO + ValidationPipe |
|
|
166
|
+
| AP-02 | Prisma no Controller | Repository |
|
|
167
|
+
| AP-03 | Lógica no Controller | Service |
|
|
168
|
+
| AP-04 | Sem DTO de saída | `UserResponseDto.from(user)` |
|
|
169
|
+
| AP-05 | Secret hardcoded | `ConfigService.getOrThrow` |
|
|
170
|
+
| AP-06 | Login sem throttler | rate limit obrigatório |
|
|
171
|
+
| AP-07 | OpenAPI à mão | `@nestjs/swagger` |
|
|
172
|
+
| AP-08 | Erros sem filter global | inconsistência |
|
|
173
|
+
|
|
174
|
+
## Segurança (com `/dare-security`)
|
|
175
|
+
|
|
176
|
+
- Hash com `@node-rs/argon2` (Argon2id)
|
|
177
|
+
- JWT RS256 público, HS256+secret≥256bits interno
|
|
178
|
+
- `helmet` middleware
|
|
179
|
+
- CORS específico
|
|
180
|
+
- Rate limit em login (5/15min)
|
|
181
|
+
- Refresh token com rotação
|
|
182
|
+
|
|
183
|
+
## CI
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm run lint
|
|
187
|
+
npm run test
|
|
188
|
+
npm run test:e2e
|
|
189
|
+
npm run build
|
|
190
|
+
npx prisma migrate deploy --dry-run
|
|
191
|
+
npm audit --audit-level=high
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## O que fazer
|
|
195
|
+
|
|
196
|
+
1. Audit:
|
|
197
|
+
```bash
|
|
198
|
+
grep -rn "@Body()" src/ | grep -v "Dto"
|
|
199
|
+
grep -rn "prisma\." src/*/controllers/
|
|
200
|
+
```
|
|
201
|
+
2. Migrar `@Body() body: any` → DTO com class-validator
|
|
202
|
+
3. Extrair lógica de Controllers → Services
|
|
203
|
+
4. Encapsular Prisma em Repositories
|
|
204
|
+
5. Configurar Swagger + ThrottlerModule + global filter
|
|
205
|
+
|
|
206
|
+
$ARGUMENTS
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
Skill MIT — parte do DARE Method.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# /skill-rails-api
|
|
2
|
+
|
|
3
|
+
Padrões DARE para APIs em Ruby on Rails 8 — API mode, ActiveRecord, Solid Queue, Solid Cable, Action Cable, strong params, Services, serializers (Blueprinter/Alba), Devise/JWT, rack-attack, rswag.
|
|
4
|
+
|
|
5
|
+
## Como usar
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/skill-rails-api # audita projeto Rails
|
|
9
|
+
/skill-rails-api scaffold users # gera controller + service + blueprint + tests
|
|
10
|
+
/skill-rails-api migrate-controllers # extrai lógica de controllers para services
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Stack canônica
|
|
14
|
+
|
|
15
|
+
- Ruby 3.3+
|
|
16
|
+
- Rails 8.x (`rails new --api`)
|
|
17
|
+
- PostgreSQL 16
|
|
18
|
+
- Solid Queue (jobs) + Solid Cable (websockets) — built-in Rails 8
|
|
19
|
+
- Devise + devise-jwt (ou JWT puro)
|
|
20
|
+
- Pundit ou CanCanCan (autorização)
|
|
21
|
+
- Blueprinter ou Alba (serializers)
|
|
22
|
+
- rack-attack (rate limit)
|
|
23
|
+
- rswag (OpenAPI)
|
|
24
|
+
- RSpec + FactoryBot + Faker
|
|
25
|
+
- Rubocop rails-omakase
|
|
26
|
+
- bundler-audit (CVEs)
|
|
27
|
+
|
|
28
|
+
## Layered Design
|
|
29
|
+
|
|
30
|
+
| Camada | Pasta Rails |
|
|
31
|
+
|---|---|
|
|
32
|
+
| Handler | `app/controllers/api/v1/` |
|
|
33
|
+
| Service | `app/services/` |
|
|
34
|
+
| Repository | `app/repositories/` (opcional) |
|
|
35
|
+
| Model | `app/models/` |
|
|
36
|
+
| Presenter | `app/blueprints/` ou `app/serializers/` |
|
|
37
|
+
|
|
38
|
+
## Controllers
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
module Api::V1
|
|
42
|
+
class UsersController < ApplicationController
|
|
43
|
+
before_action :authenticate_user!
|
|
44
|
+
|
|
45
|
+
def create
|
|
46
|
+
result = RegisterUser.new(user_params).call
|
|
47
|
+
if result.success?
|
|
48
|
+
render json: UserBlueprint.render(result.user), status: :created
|
|
49
|
+
else
|
|
50
|
+
render json: { error: result.error_code }, status: :conflict
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def user_params
|
|
57
|
+
params.require(:user).permit(:email, :name, :password)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Service Object
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
class RegisterUser
|
|
67
|
+
Result = Struct.new(:success?, :user, :error_code)
|
|
68
|
+
|
|
69
|
+
def initialize(params)
|
|
70
|
+
@params = params
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def call
|
|
74
|
+
return Result.new(false, nil, 'USER_EXISTS') if User.exists?(email: @params[:email])
|
|
75
|
+
user = User.create!(@params)
|
|
76
|
+
Result.new(true, user, nil)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Model
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
class User < ApplicationRecord
|
|
85
|
+
has_secure_password
|
|
86
|
+
validates :email, presence: true, uniqueness: true, format: URI::MailTo::EMAIL_REGEXP
|
|
87
|
+
validates :name, presence: true
|
|
88
|
+
validates :password, length: { minimum: 12 }, if: :password_required?
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Blueprinter (serializer)
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
class UserBlueprint < Blueprinter::Base
|
|
96
|
+
identifier :id
|
|
97
|
+
fields :email, :name
|
|
98
|
+
field :created_at do |u| u.created_at.iso8601 end
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Auth (devise-jwt)
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
# config/initializers/devise.rb
|
|
106
|
+
config.jwt do |jwt|
|
|
107
|
+
jwt.secret = Rails.application.credentials.devise_jwt_secret_key!
|
|
108
|
+
jwt.dispatch_requests = [['POST', %r{^/api/v1/login$}]]
|
|
109
|
+
jwt.revocation_requests = [['DELETE', %r{^/api/v1/logout$}]]
|
|
110
|
+
jwt.expiration_time = 15.minutes.to_i
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## rack-attack
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
class Rack::Attack
|
|
118
|
+
throttle('login/ip', limit: 5, period: 15.minutes) do |req|
|
|
119
|
+
req.ip if req.path == '/api/v1/login' && req.post?
|
|
120
|
+
end
|
|
121
|
+
throttle('api/ip', limit: 100, period: 1.minute) do |req|
|
|
122
|
+
req.ip if req.path.start_with?('/api/')
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Solid Queue (jobs)
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
class SendWelcomeEmailJob < ApplicationJob
|
|
131
|
+
queue_as :default
|
|
132
|
+
def perform(user_id)
|
|
133
|
+
user = User.find(user_id)
|
|
134
|
+
UserMailer.welcome(user).deliver_now
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Solid Cable + Action Cable
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
class NotificationsChannel < ApplicationCable::Channel
|
|
143
|
+
def subscribed
|
|
144
|
+
stream_for current_user
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Broadcast
|
|
149
|
+
NotificationsChannel.broadcast_to(user, type: 'message.sent.v1', payload: {...})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## OpenAPI (rswag)
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
# spec/integration/users_spec.rb
|
|
156
|
+
path '/api/v1/users' do
|
|
157
|
+
post 'Creates a user' do
|
|
158
|
+
parameter name: :user, in: :body, schema: { ... }
|
|
159
|
+
response '201', 'user created' do ... end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# rake rswag:specs:swaggerize → swagger/v1/swagger.yaml
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Testes RSpec
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
# Service
|
|
170
|
+
RSpec.describe RegisterUser do
|
|
171
|
+
it 'falha se email já existe' do
|
|
172
|
+
User.create!(email: 'x@y.com', name: 'X', password: 'longsecret123')
|
|
173
|
+
result = described_class.new(email: 'x@y.com', name: 'Y', password: 'longsecret123').call
|
|
174
|
+
expect(result.error_code).to eq 'USER_EXISTS'
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Request
|
|
179
|
+
RSpec.describe 'POST /api/v1/users', type: :request do
|
|
180
|
+
it 'cria com sucesso' do
|
|
181
|
+
post '/api/v1/users', params: { user: {...} }, headers: auth_headers
|
|
182
|
+
expect(response).to have_http_status(:created)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Antipatterns
|
|
188
|
+
|
|
189
|
+
| AP | Antipattern | Correção |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| AP-01 | Query AR no controller | Service object |
|
|
192
|
+
| AP-02 | Lógica no controller | Service object |
|
|
193
|
+
| AP-03 | `render json: user` | Blueprinter/Alba |
|
|
194
|
+
| AP-04 | Fat Model | Concerns + Services |
|
|
195
|
+
| AP-05 | Skip strong params | `permit(...)` sempre |
|
|
196
|
+
| AP-06 | Sem rack-attack em login | rate limit obrigatório |
|
|
197
|
+
| AP-07 | JWT secret em código | `credentials.yml.enc` |
|
|
198
|
+
| AP-08 | Logs com PII | `filter_parameters` |
|
|
199
|
+
| AP-09 | N+1 sem `includes` | `bullet` gem em dev |
|
|
200
|
+
| AP-10 | Sem `--api` no Rails new | use modo API |
|
|
201
|
+
|
|
202
|
+
## Segurança
|
|
203
|
+
|
|
204
|
+
- `has_secure_password` (BCrypt default Rails OK; trocar para Argon2 se quiser)
|
|
205
|
+
- `force_ssl = true` em prod
|
|
206
|
+
- `secure_headers` gem ou nativo Rails 8
|
|
207
|
+
- `rack-cors` com origens específicas
|
|
208
|
+
- `filter_parameters` para password/token/secret
|
|
209
|
+
- bundler-audit no CI
|
|
210
|
+
|
|
211
|
+
## CI
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
bundle exec rubocop
|
|
215
|
+
bundle exec rspec
|
|
216
|
+
bundle exec bundler-audit check --update
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## O que fazer
|
|
220
|
+
|
|
221
|
+
1. Audit:
|
|
222
|
+
```bash
|
|
223
|
+
grep -rn "\.where\|\.create" app/controllers/
|
|
224
|
+
grep -rn "render json: @" app/controllers/
|
|
225
|
+
grep -rn "params\[:" app/controllers/ | grep -v "permit"
|
|
226
|
+
```
|
|
227
|
+
2. Lógica > 10 linhas no controller → Service `.call`
|
|
228
|
+
3. `render json: object` → `BlueprintRender(object)`
|
|
229
|
+
4. Configurar rack-attack + filter_parameters
|
|
230
|
+
5. Documentar endpoints com rswag + gerar OpenAPI
|
|
231
|
+
|
|
232
|
+
$ARGUMENTS
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
Skill MIT — parte do DARE Method.
|