@polymorphism-tech/morph-spec 7.9.3 → 8.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/CLAUDE.md +2 -2
- package/README.md +31 -2
- package/bin/morph-spec.js +50 -2
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +38 -1
- package/docs/QUICKSTART.md +60 -1
- package/framework/CLAUDE.md +13 -2
- package/framework/MORPH.md +38 -2
- package/framework/agents.json +22 -3
- package/framework/commands/commit.md +166 -166
- package/framework/commands/morph-hotfix.md +39 -0
- package/framework/commands/morph-infra.md +215 -215
- package/framework/commands/morph-preflight.md +205 -205
- package/framework/commands/morph-troubleshoot.md +124 -124
- package/framework/hooks/README.md +25 -10
- package/framework/hooks/claude-code/core/gate-guard.js +4 -2
- package/framework/hooks/claude-code/core/loop-detect.js +2 -1
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +14 -5
- package/framework/hooks/claude-code/core/tasks-json-guard.js +4 -2
- package/framework/hooks/claude-code/core/validate-completion.js +1 -1
- package/framework/hooks/claude-code/core/worktree-context.js +160 -0
- package/framework/hooks/claude-code/statusline.py +679 -679
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/activity-logger.js +160 -160
- package/framework/hooks/shared/build-check.js +2 -2
- package/framework/hooks/shared/hook-response.js +17 -5
- package/framework/hooks/shared/payload-utils.js +42 -0
- package/framework/hooks/shared/state-reader.js +22 -2
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/rubrics/backend.json +41 -41
- package/framework/rules/ai-agents-standards.md +45 -45
- package/framework/rules/dotnet-templates.md +86 -86
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +27 -27
- package/framework/rules/neon-standards.md +25 -25
- package/framework/rules/nextjs-standards.md +20 -20
- package/framework/rules/nextjs-templates.md +40 -40
- package/framework/schemas/feature.schema.json +95 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +39 -8
- package/framework/skills/core/morph-brainstorming/SKILL.md +9 -1
- package/framework/skills/core/morph-eval/SKILL.md +6 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +136 -0
- package/framework/skills/core/morph-implement/SKILL.md +8 -4
- package/framework/skills/core/morph-plan/SKILL.md +5 -1
- package/framework/skills/core/morph-proposal/SKILL.md +24 -1
- package/framework/skills/core/morph-review/SKILL.md +3 -2
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +7 -2
- package/framework/standards/ai-agents/agent-session.md +160 -160
- package/framework/standards/ai-agents/batching.md +193 -193
- package/framework/standards/ai-agents/context-providers.md +188 -188
- package/framework/standards/ai-agents/mcp-server.md +250 -250
- package/framework/standards/ai-agents/mcp-tools.md +197 -197
- package/framework/standards/ai-agents/middleware-patterns.md +207 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
- package/framework/standards/ai-agents/modalities-vision.md +201 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
- package/framework/standards/ai-agents/production.md +275 -275
- package/framework/standards/ai-agents/prompts-in-db.md +214 -214
- package/framework/standards/ai-agents/providers/model-registry.md +158 -158
- package/framework/standards/ai-agents/service-tier-flex.md +135 -135
- package/framework/standards/ai-agents/structured-output.md +249 -249
- package/framework/standards/ai-agents/sweet-spot.md +109 -109
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +45 -3
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +0 -62
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/index.js +13 -13
- package/src/commands/mcp/mcp-setup.js +519 -519
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +4 -2
- package/src/commands/project/init.js +46 -2
- package/src/commands/project/update.js +5 -3
- package/src/commands/project/worktree.js +1005 -46
- package/src/commands/state/advance.js +17 -2
- package/src/commands/state/approve.js +18 -1
- package/src/commands/state/create.js +158 -14
- package/src/commands/state/retype.js +126 -0
- package/src/commands/state/score.js +4 -0
- package/src/commands/tasks/dag.js +32 -0
- package/src/commands/validation/index.js +1 -0
- package/src/commands/validation/validate-feature.js +33 -5
- package/src/commands/validation/verify.js +190 -0
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/state/state-manager.js +35 -10
- package/src/core/templates/template-renderer.js +477 -477
- package/src/core/work-types.js +79 -0
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/HookInspector.jsx +418 -418
- package/src/dashboard/app/pages/LiveState.jsx +11 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/hooks-scanner.js +41 -41
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/scanner/standards-scanner.js +26 -26
- package/src/dashboard/scanner/state-scanner.js +42 -0
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/metrics-aggregator.js +56 -56
- package/src/lib/agents/metrics-calculator.js +125 -125
- package/src/lib/agents/output-collector.js +73 -73
- package/src/lib/detectors/claude-config-detector.js +152 -152
- package/src/lib/detectors/design-system-detector.js +231 -231
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/generators/architecture-scanner.js +639 -639
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/index.js +13 -13
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/installers/memory-installer.js +1 -1
- package/src/lib/risk-detector.js +16 -0
- package/src/lib/tasks/dispatch-planner.js +122 -4
- package/src/lib/tasks/mermaid-renderer.js +132 -0
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/test-runner.js +27 -6
- package/src/lib/validators/architecture/architecture-validator.js +259 -259
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/packages/package-validator.js +347 -347
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/validation-runner.js +349 -349
- package/src/lib/validators/verify-runner.js +322 -0
- package/src/lib/work-type-detector.js +143 -0
- package/src/scripts/setup-infra.js +32 -5
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +10 -6
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +43 -0
- package/src/utils/hooks-installer.js +41 -11
- package/src/utils/skills-installer.js +174 -174
- package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
- package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
- package/src/lib/tasks/wave-resolver.js +0 -353
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
# Sweet Spot — When to use what in MAF
|
|
2
|
-
|
|
3
|
-
> **Scope:** stacks=["dotnet"]
|
|
4
|
-
> **Layer:** 0 (always-load via persona maf-expert)
|
|
5
|
-
> **Keywords:** sweet spot, when to use agent, agent vs workflow, when not to use workflow, MAF decision, simple agent
|
|
6
|
-
> **Read by Claude in:** plan (decide arquitetura da feature antes de quebrar em tasks) + implement (validar que a arquitetura ainda é a mais simples possível)
|
|
7
|
-
|
|
8
|
-
**Verified against:** MAF Tier List research (2026-05-18) + Microsoft Learn docs. Last-verified: 2026-05-19.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## The 4-feature rule
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
95% of production AI agents need ONLY:
|
|
16
|
-
|
|
17
|
-
1. Chat (the LLM call)
|
|
18
|
-
2. Structured Output (typed records, not free text)
|
|
19
|
-
3. Tools (typed functions the agent can call)
|
|
20
|
-
4. RAG Custom (pgvector tool, not hosted RAG)
|
|
21
|
-
|
|
22
|
-
If your feature needs ONLY these 4 → use a single agent. Don't reach for workflows.
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Apply this rule before deciding multi-agent or workflow. Most of the time the answer is "single agent".
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Decision tree (Claude reads this in plan phase)
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
Step 1 — Does the task even need an LLM?
|
|
33
|
-
├─ "Can a deterministic function solve this?" → YES → write the function, no agent
|
|
34
|
-
└─ NO → continue
|
|
35
|
-
|
|
36
|
-
Step 2 — Single agent vs multi-agent?
|
|
37
|
-
├─ One topic + one decision → single agent (DEFAULT)
|
|
38
|
-
├─ Multiple specialized topics with clear handoff → multi-agent
|
|
39
|
-
│ └─ Prefer Agent-as-Tool pattern (one agent uses another as a tool)
|
|
40
|
-
│ └─ See ai-agents-multi-agent-patterns
|
|
41
|
-
└─ "I'm not sure" → start with single agent, escalate if needed
|
|
42
|
-
|
|
43
|
-
Step 3 — Need a Workflow?
|
|
44
|
-
├─ The feature has fan-in/out (many inputs aggregated) → maybe workflow
|
|
45
|
-
│ └─ Confirm: can you write it as a foreach + LINQ aggregate?
|
|
46
|
-
│ └─ YES → write the foreach, NO workflow
|
|
47
|
-
│ └─ NO → workflow may help
|
|
48
|
-
├─ Long-running with human-in-the-loop checkpoints → maybe workflow
|
|
49
|
-
│ └─ See ai-agents-workflows (Onda 2)
|
|
50
|
-
└─ Otherwise → NO workflow
|
|
51
|
-
|
|
52
|
-
Step 4 — Which provider?
|
|
53
|
-
├─ ALWAYS direct models — never a hosted agent service
|
|
54
|
-
├─ Provider choice (OpenAI / Google / Anthropic / Ollama) lives in the
|
|
55
|
-
│ project's Model Registry, not hardcoded → see ai-agents-providers-model-registry
|
|
56
|
-
└─ Hosted services (Foundry Agent Service, Vertex AI hosted) → NOT used
|
|
57
|
-
|
|
58
|
-
Step 5 — RAG approach?
|
|
59
|
-
├─ Need control over chunking/embedding/storage? → RAG custom (pgvector)
|
|
60
|
-
│ └─ See ai-agents-rag-custom-pgvector (Onda 2)
|
|
61
|
-
└─ Don't reach for hosted RAG — loses control
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Anti-patterns (what Claude should NOT do)
|
|
67
|
-
|
|
68
|
-
| Anti-pattern | Why it's wrong | What to do instead |
|
|
69
|
-
|--------------|----------------|---------------------|
|
|
70
|
-
| Reach for Workflow when a function does the job | "Tudo que Workflows fazem, código C# normal faz com menos complexidade" — pesquisa | Write the function. Wrap in a tool if an agent needs to call it. |
|
|
71
|
-
| Use a hosted agent service | Hosted is "always 6-9 months behind" on models + removes control | Direct models always (OpenAI / Google / Anthropic / Ollama) |
|
|
72
|
-
| Upload PDFs to a hosted RAG | Loses control of chunking + embedding + storage | Custom RAG with pgvector. See ai-agents-rag-custom-pgvector |
|
|
73
|
-
| Add Memory/Session because "agents need state" | If not building a chatbot, don't add chat state | Skip. Add when a real chat UI exists. |
|
|
74
|
-
| Use Semantic Kernel for new features | SK is superseded by MAF | Use Microsoft.Agents.AI. See ai-agents-setup |
|
|
75
|
-
| Return free text and parse it downstream | LLM output is non-deterministic | Structured Output. See ai-agents-structured-output (Onda 2) |
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## When the 4-feature rule does NOT apply
|
|
80
|
-
|
|
81
|
-
You may need to step outside the sweet spot when:
|
|
82
|
-
- The feature genuinely fans-out (e.g., monthly report aggregating N items) → workflow with fan-in
|
|
83
|
-
- The feature needs vision/audio/image-gen → modality-specific standards (see ai-agents-modalities-* — Onda 3)
|
|
84
|
-
|
|
85
|
-
**Even in these cases:** the core of the agent still follows the 4-feature rule. Only the orchestration layer changes.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## Checklist (verifiable by morph-eval)
|
|
90
|
-
|
|
91
|
-
- [ ] Was each agent designed as single-agent BEFORE reaching for multi-agent? (Check `decisions.md`.)
|
|
92
|
-
- [ ] If a Workflow is used, is fan-in/out or HITL checkpointing genuinely present? Or could `foreach + LINQ` replace it?
|
|
93
|
-
- [ ] Are the 4 features (Chat, Structured Output, Tools, RAG Custom) all present where applicable?
|
|
94
|
-
- [ ] Are hosted agent services avoided?
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## References
|
|
99
|
-
|
|
100
|
-
- `ai-agents-setup` — packages and DI
|
|
101
|
-
- `ai-agents-providers-model-registry` — provider-agnostic config
|
|
102
|
-
- `ai-agents-structured-output` (Onda 2)
|
|
103
|
-
- `ai-agents-multi-agent-patterns` (Onda 2)
|
|
104
|
-
- `ai-agents-workflows` (Onda 2, reframed)
|
|
105
|
-
- `ai-agents-rag-custom-pgvector` (Onda 2)
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
*MORPH-SPEC by Polymorphism Tech — ai-agents/sweet-spot.md v1.0 (2026-05-19)*
|
|
1
|
+
# Sweet Spot — When to use what in MAF
|
|
2
|
+
|
|
3
|
+
> **Scope:** stacks=["dotnet"]
|
|
4
|
+
> **Layer:** 0 (always-load via persona maf-expert)
|
|
5
|
+
> **Keywords:** sweet spot, when to use agent, agent vs workflow, when not to use workflow, MAF decision, simple agent
|
|
6
|
+
> **Read by Claude in:** plan (decide arquitetura da feature antes de quebrar em tasks) + implement (validar que a arquitetura ainda é a mais simples possível)
|
|
7
|
+
|
|
8
|
+
**Verified against:** MAF Tier List research (2026-05-18) + Microsoft Learn docs. Last-verified: 2026-05-19.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The 4-feature rule
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
95% of production AI agents need ONLY:
|
|
16
|
+
|
|
17
|
+
1. Chat (the LLM call)
|
|
18
|
+
2. Structured Output (typed records, not free text)
|
|
19
|
+
3. Tools (typed functions the agent can call)
|
|
20
|
+
4. RAG Custom (pgvector tool, not hosted RAG)
|
|
21
|
+
|
|
22
|
+
If your feature needs ONLY these 4 → use a single agent. Don't reach for workflows.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Apply this rule before deciding multi-agent or workflow. Most of the time the answer is "single agent".
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Decision tree (Claude reads this in plan phase)
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Step 1 — Does the task even need an LLM?
|
|
33
|
+
├─ "Can a deterministic function solve this?" → YES → write the function, no agent
|
|
34
|
+
└─ NO → continue
|
|
35
|
+
|
|
36
|
+
Step 2 — Single agent vs multi-agent?
|
|
37
|
+
├─ One topic + one decision → single agent (DEFAULT)
|
|
38
|
+
├─ Multiple specialized topics with clear handoff → multi-agent
|
|
39
|
+
│ └─ Prefer Agent-as-Tool pattern (one agent uses another as a tool)
|
|
40
|
+
│ └─ See ai-agents-multi-agent-patterns
|
|
41
|
+
└─ "I'm not sure" → start with single agent, escalate if needed
|
|
42
|
+
|
|
43
|
+
Step 3 — Need a Workflow?
|
|
44
|
+
├─ The feature has fan-in/out (many inputs aggregated) → maybe workflow
|
|
45
|
+
│ └─ Confirm: can you write it as a foreach + LINQ aggregate?
|
|
46
|
+
│ └─ YES → write the foreach, NO workflow
|
|
47
|
+
│ └─ NO → workflow may help
|
|
48
|
+
├─ Long-running with human-in-the-loop checkpoints → maybe workflow
|
|
49
|
+
│ └─ See ai-agents-workflows (Onda 2)
|
|
50
|
+
└─ Otherwise → NO workflow
|
|
51
|
+
|
|
52
|
+
Step 4 — Which provider?
|
|
53
|
+
├─ ALWAYS direct models — never a hosted agent service
|
|
54
|
+
├─ Provider choice (OpenAI / Google / Anthropic / Ollama) lives in the
|
|
55
|
+
│ project's Model Registry, not hardcoded → see ai-agents-providers-model-registry
|
|
56
|
+
└─ Hosted services (Foundry Agent Service, Vertex AI hosted) → NOT used
|
|
57
|
+
|
|
58
|
+
Step 5 — RAG approach?
|
|
59
|
+
├─ Need control over chunking/embedding/storage? → RAG custom (pgvector)
|
|
60
|
+
│ └─ See ai-agents-rag-custom-pgvector (Onda 2)
|
|
61
|
+
└─ Don't reach for hosted RAG — loses control
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Anti-patterns (what Claude should NOT do)
|
|
67
|
+
|
|
68
|
+
| Anti-pattern | Why it's wrong | What to do instead |
|
|
69
|
+
|--------------|----------------|---------------------|
|
|
70
|
+
| Reach for Workflow when a function does the job | "Tudo que Workflows fazem, código C# normal faz com menos complexidade" — pesquisa | Write the function. Wrap in a tool if an agent needs to call it. |
|
|
71
|
+
| Use a hosted agent service | Hosted is "always 6-9 months behind" on models + removes control | Direct models always (OpenAI / Google / Anthropic / Ollama) |
|
|
72
|
+
| Upload PDFs to a hosted RAG | Loses control of chunking + embedding + storage | Custom RAG with pgvector. See ai-agents-rag-custom-pgvector |
|
|
73
|
+
| Add Memory/Session because "agents need state" | If not building a chatbot, don't add chat state | Skip. Add when a real chat UI exists. |
|
|
74
|
+
| Use Semantic Kernel for new features | SK is superseded by MAF | Use Microsoft.Agents.AI. See ai-agents-setup |
|
|
75
|
+
| Return free text and parse it downstream | LLM output is non-deterministic | Structured Output. See ai-agents-structured-output (Onda 2) |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## When the 4-feature rule does NOT apply
|
|
80
|
+
|
|
81
|
+
You may need to step outside the sweet spot when:
|
|
82
|
+
- The feature genuinely fans-out (e.g., monthly report aggregating N items) → workflow with fan-in
|
|
83
|
+
- The feature needs vision/audio/image-gen → modality-specific standards (see ai-agents-modalities-* — Onda 3)
|
|
84
|
+
|
|
85
|
+
**Even in these cases:** the core of the agent still follows the 4-feature rule. Only the orchestration layer changes.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Checklist (verifiable by morph-eval)
|
|
90
|
+
|
|
91
|
+
- [ ] Was each agent designed as single-agent BEFORE reaching for multi-agent? (Check `decisions.md`.)
|
|
92
|
+
- [ ] If a Workflow is used, is fan-in/out or HITL checkpointing genuinely present? Or could `foreach + LINQ` replace it?
|
|
93
|
+
- [ ] Are the 4 features (Chat, Structured Output, Tools, RAG Custom) all present where applicable?
|
|
94
|
+
- [ ] Are hosted agent services avoided?
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## References
|
|
99
|
+
|
|
100
|
+
- `ai-agents-setup` — packages and DI
|
|
101
|
+
- `ai-agents-providers-model-registry` — provider-agnostic config
|
|
102
|
+
- `ai-agents-structured-output` (Onda 2)
|
|
103
|
+
- `ai-agents-multi-agent-patterns` (Onda 2)
|
|
104
|
+
- `ai-agents-workflows` (Onda 2, reframed)
|
|
105
|
+
- `ai-agents-rag-custom-pgvector` (Onda 2)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/sweet-spot.md v1.0 (2026-05-19)*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Scope:** universal
|
|
4
4
|
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:** neon auth, better auth, authentication, nextjs, user management, session, JWT, trusted origins, EdDSA, Ed25519, BouncyCastle, signature validator, jwks, middleware runtime, api prefix
|
|
5
|
+
> **Keywords:** neon auth, better auth, authentication, nextjs, user management, session, JWT, trusted origins, EdDSA, Ed25519, BouncyCastle, signature validator, jwks, middleware runtime, api prefix, data api, postgrest, bypass backend, direct database access
|
|
6
6
|
> **Load When:** neon auth or authentication keywords detected
|
|
7
7
|
|
|
8
8
|
**Verified against:** Neon Auth — @neondatabase/auth + @neondatabase/auth-ui (Better Auth). Last-verified: 2026-07-13.
|
|
@@ -37,13 +37,42 @@ Neon Auth provides:
|
|
|
37
37
|
1. **Database-First**: Auth data lives in `neon_auth` schema — no external service to sync
|
|
38
38
|
2. **Branch Isolation**: Each Neon branch gets isolated users, sessions, and auth config
|
|
39
39
|
3. **Server-First**: Use server-side auth checks with `auth.getSession()`
|
|
40
|
-
4. **JWT for RLS**: Tokens contain `sub` (user ID), `email`, `role: "authenticated"` for database
|
|
40
|
+
4. **JWT for RLS**: Tokens contain `sub` (user ID), `email`, `role: "authenticated"` for database
|
|
41
|
+
policies — used only inside the justified Data API exception path, not by default (see
|
|
42
|
+
[Data Access Boundary](#data-access-boundary--backend-first-by-default))
|
|
41
43
|
5. **No OIDC discovery**: Neon Auth does **not** expose `/.well-known/openid-configuration` — only the
|
|
42
44
|
raw `/.well-known/jwks.json`. Never assume `options.Authority` alone gives you working discovery on
|
|
43
45
|
the .NET side (see [.NET Backend Integration](#net-backend-integration)).
|
|
44
46
|
|
|
45
47
|
---
|
|
46
48
|
|
|
49
|
+
## Data Access Boundary — Backend-First by Default
|
|
50
|
+
|
|
51
|
+
The Next.js frontend has exactly one inherent reason to talk to Neon directly: Neon Auth's own
|
|
52
|
+
client SDK (`authClient`, `useSession`, `auth.getSession()`) — that's Better Auth's session/JWT
|
|
53
|
+
mechanism, not a business-data call. Every read or write of application/business data goes through
|
|
54
|
+
the .NET backend instead, either as a server-direct fetch or a client-via-proxy call (see
|
|
55
|
+
[API Prefix Convention](#api-prefix-convention--server-direct-vs-client-proxy) for the mechanics).
|
|
56
|
+
|
|
57
|
+
**Rule:** the frontend does not query Neon directly for business data — not via a Neon Data API
|
|
58
|
+
(REST-over-Postgres) endpoint, not via a browser-held connection string, not via any other bypass
|
|
59
|
+
of the .NET backend. The .NET backend is the sole gateway to business data, for the same reason
|
|
60
|
+
`architecture/vertical-slice/vertical-slice.md` makes the backend the sole gateway to the database
|
|
61
|
+
in general: handler-level authorization, no business rules duplicated on two sides of the wire, one
|
|
62
|
+
place to audit.
|
|
63
|
+
|
|
64
|
+
**The exception process:** a feature that genuinely needs the frontend to reach Neon directly for
|
|
65
|
+
business data (for example, via the Neon MCP `provision_neon_data_api` tool) is an exception, not a
|
|
66
|
+
default — treat it exactly like vertical-slice.md's "Stores dedicados (exceção justificada)" clause:
|
|
67
|
+
document the justification in the feature's `decisions.md` before writing the code. A tool being
|
|
68
|
+
available is not, by itself, justification.
|
|
69
|
+
|
|
70
|
+
See [Row-Level Security Integration](#row-level-security-integration) for how to constrain a
|
|
71
|
+
Data-API-exception session once one exists — those RLS policies are not a signal that the frontend
|
|
72
|
+
should have direct access by default.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
47
76
|
## Installation & Setup
|
|
48
77
|
|
|
49
78
|
### Install Packages
|
|
@@ -299,6 +328,10 @@ export async function GET() {
|
|
|
299
328
|
|
|
300
329
|
## API Prefix Convention — Server-Direct vs Client-Proxy
|
|
301
330
|
|
|
331
|
+
> Assumes the [Data Access Boundary](#data-access-boundary--backend-first-by-default) rule: all
|
|
332
|
+
> business data routes through the backend by default. This section covers only the *mechanics* of
|
|
333
|
+
> doing so from each render context.
|
|
334
|
+
|
|
302
335
|
A Next.js app talking to a Neon-Auth-protected .NET backend fetches from **two different places**
|
|
303
336
|
depending on where the code runs:
|
|
304
337
|
|
|
@@ -616,7 +649,13 @@ app.Run();
|
|
|
616
649
|
|
|
617
650
|
## Row-Level Security Integration
|
|
618
651
|
|
|
619
|
-
|
|
652
|
+
> Defense-in-depth on a JWT-scoped Postgres session — the Data API exception above, not a directive
|
|
653
|
+
> to call Neon from the browser for business data by default. See
|
|
654
|
+
> [Data Access Boundary](#data-access-boundary--backend-first-by-default).
|
|
655
|
+
|
|
656
|
+
When a Data API endpoint is provisioned (the justified exception above), Neon Auth JWT tokens are
|
|
657
|
+
automatically validated by it. The `auth.user_id()` function (via `pg_session_jwt` extension)
|
|
658
|
+
exposes the authenticated user ID from the JWT `sub` claim to any RLS policy on that session.
|
|
620
659
|
|
|
621
660
|
```sql
|
|
622
661
|
-- Enable RLS
|
|
@@ -673,6 +712,9 @@ Each Neon branch gets its own isolated auth data. This means:
|
|
|
673
712
|
service silently becomes a phantom `[FromBody]` parameter and crashes the app at startup
|
|
674
713
|
10. **On .NET, never rely on `options.Authority` alone** — Neon Auth has no OIDC discovery document;
|
|
675
714
|
fetch and cache the JWKS yourself and verify EdDSA signatures manually (BouncyCastle)
|
|
715
|
+
11. **Route business data through the .NET backend by default** — the frontend's only inherent
|
|
716
|
+
reason to talk to Neon directly is its own auth/session SDK; a direct Data API call needs a
|
|
717
|
+
`decisions.md` ADR (see [Data Access Boundary](#data-access-boundary--backend-first-by-default))
|
|
676
718
|
|
|
677
719
|
---
|
|
678
720
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<templateSet group="MORPH Templates">
|
|
2
|
+
<template name="morph-pascal" value="{{pascalCase $FEATURE_NAME$}}" description="Convert to PascalCase (UserAuth)" toReformat="false" toShortenFQNames="true">
|
|
3
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
4
|
+
<context>
|
|
5
|
+
<option name="OTHER" value="true" />
|
|
6
|
+
</context>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<template name="morph-camel" value="{{camelCase $FEATURE_NAME$}}" description="Convert to camelCase (userAuth)" toReformat="false" toShortenFQNames="true">
|
|
10
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
11
|
+
<context>
|
|
12
|
+
<option name="OTHER" value="true" />
|
|
13
|
+
</context>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<template name="morph-snake" value="{{snakeCase $FEATURE_NAME$}}" description="Convert to snake_case (user_auth)" toReformat="false" toShortenFQNames="true">
|
|
17
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
18
|
+
<context>
|
|
19
|
+
<option name="OTHER" value="true" />
|
|
20
|
+
</context>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<template name="morph-title" value="{{titleCase $FEATURE_NAME$}}" description="Convert to Title Case (User Auth)" toReformat="false" toShortenFQNames="true">
|
|
24
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
25
|
+
<context>
|
|
26
|
+
<option name="OTHER" value="true" />
|
|
27
|
+
</context>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<template name="morph-kebab" value="{{kebabCase $FEATURE_NAME$}}" description="Convert to kebab-case (user-auth)" toReformat="false" toShortenFQNames="true">
|
|
31
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
32
|
+
<context>
|
|
33
|
+
<option name="OTHER" value="true" />
|
|
34
|
+
</context>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<template name="morph-if" value="{{#if $CONDITION$}} $END$ {{/if}}" description="Handlebars if block" toReformat="false" toShortenFQNames="true">
|
|
38
|
+
<variable name="CONDITION" expression="" defaultValue=""condition"" alwaysStopAt="true" />
|
|
39
|
+
<context>
|
|
40
|
+
<option name="OTHER" value="true" />
|
|
41
|
+
</context>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<template name="morph-if-else" value="{{#if $CONDITION$}} $TRUE_BRANCH$ {{else}} $FALSE_BRANCH$ {{/if}}" description="Handlebars if-else block" toReformat="false" toShortenFQNames="true">
|
|
45
|
+
<variable name="CONDITION" expression="" defaultValue=""condition"" alwaysStopAt="true" />
|
|
46
|
+
<variable name="TRUE_BRANCH" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
47
|
+
<variable name="FALSE_BRANCH" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
48
|
+
<context>
|
|
49
|
+
<option name="OTHER" value="true" />
|
|
50
|
+
</context>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<template name="morph-each" value="{{#each $ITEMS$}} {{this.$PROPERTY$}} {{/each}}" description="Handlebars each loop" toReformat="false" toShortenFQNames="true">
|
|
54
|
+
<variable name="ITEMS" expression="" defaultValue=""items"" alwaysStopAt="true" />
|
|
55
|
+
<variable name="PROPERTY" expression="" defaultValue=""name"" alwaysStopAt="true" />
|
|
56
|
+
<context>
|
|
57
|
+
<option name="OTHER" value="true" />
|
|
58
|
+
</context>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<template name="morph-service" value="namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services; public class {{pascalCase FEATURE_NAME}}Service( I{{pascalCase FEATURE_NAME}}Repository repository, ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service { public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default) { $END$ } }" description="MORPH .NET Service class template" toReformat="false" toShortenFQNames="true">
|
|
62
|
+
<context>
|
|
63
|
+
<option name="C_SHARP" value="true" />
|
|
64
|
+
</context>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<template name="morph-repository" value="public class {{pascalCase FEATURE_NAME}}Repository(AppDbContext context) : I{{pascalCase FEATURE_NAME}}Repository { private readonly DbSet<{{pascalCase FEATURE_NAME}}> _dbSet = context.Set<{{pascalCase FEATURE_NAME}}>(); public async Task<{{pascalCase FEATURE_NAME}}?> GetByIdAsync(int id, CancellationToken cancellationToken = default) { $END$ } }" description="MORPH .NET Repository class template" toReformat="false" toShortenFQNames="true">
|
|
68
|
+
<context>
|
|
69
|
+
<option name="C_SHARP" value="true" />
|
|
70
|
+
</context>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<template name="morph-component" value="@page "/{{kebabCase FEATURE_NAME}}" @inject I{{pascalCase FEATURE_NAME}}Service {{pascalCase FEATURE_NAME}}Service @inject ILogger<{{pascalCase FEATURE_NAME}}Page> Logger <PageTitle>{{pascalCase FEATURE_NAME}}</PageTitle> <div class="container-fluid"> $END$ </div> @code { }" description="MORPH Blazor component template" toReformat="false" toShortenFQNames="true">
|
|
74
|
+
<context>
|
|
75
|
+
<option name="HTML" value="true" />
|
|
76
|
+
</context>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<template name="morph-migration" value="public partial class Add{{pascalCase FEATURE_NAME}} : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "{{pascalCase FEATURE_NAME}}s", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), $END$ }, constraints: table => { table.PrimaryKey("PK_{{pascalCase FEATURE_NAME}}s", x => x.Id); }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable(name: "{{pascalCase FEATURE_NAME}}s"); } }" description="MORPH EF Core migration template" toReformat="false" toShortenFQNames="true">
|
|
80
|
+
<context>
|
|
81
|
+
<option name="C_SHARP" value="true" />
|
|
82
|
+
</context>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<template name="morph-ts-contract" value="export interface {{pascalCase FEATURE_NAME}} { id: number; $FIELD1$ } export interface Create{{pascalCase FEATURE_NAME}}Request { $FIELD2$ } export interface Update{{pascalCase FEATURE_NAME}}Request { $END$ }" description="MORPH TypeScript contract template" toReformat="false" toShortenFQNames="true">
|
|
86
|
+
<variable name="FIELD1" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
87
|
+
<variable name="FIELD2" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
88
|
+
<context>
|
|
89
|
+
<option name="TypeScript" value="true" />
|
|
90
|
+
</context>
|
|
91
|
+
</template>
|
|
92
|
+
</templateSet>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"MORPH Template - PascalCase": {
|
|
3
|
+
"prefix": "morph-pascal",
|
|
4
|
+
"body": ["{{pascalCase ${1:FEATURE_NAME}}}"],
|
|
5
|
+
"description": "Convert to PascalCase (UserAuth)"
|
|
6
|
+
},
|
|
7
|
+
"MORPH Template - camelCase": {
|
|
8
|
+
"prefix": "morph-camel",
|
|
9
|
+
"body": ["{{camelCase ${1:FEATURE_NAME}}}"],
|
|
10
|
+
"description": "Convert to camelCase (userAuth)"
|
|
11
|
+
},
|
|
12
|
+
"MORPH Template - snake_case": {
|
|
13
|
+
"prefix": "morph-snake",
|
|
14
|
+
"body": ["{{snakeCase ${1:FEATURE_NAME}}}"],
|
|
15
|
+
"description": "Convert to snake_case (user_auth)"
|
|
16
|
+
},
|
|
17
|
+
"MORPH Template - Title Case": {
|
|
18
|
+
"prefix": "morph-title",
|
|
19
|
+
"body": ["{{titleCase ${1:FEATURE_NAME}}}"],
|
|
20
|
+
"description": "Convert to Title Case (User Auth)"
|
|
21
|
+
},
|
|
22
|
+
"MORPH Template - kebab-case": {
|
|
23
|
+
"prefix": "morph-kebab",
|
|
24
|
+
"body": ["{{kebabCase ${1:FEATURE_NAME}}}"],
|
|
25
|
+
"description": "Convert to kebab-case (user-auth)"
|
|
26
|
+
},
|
|
27
|
+
"MORPH Template - UPPER_SNAKE_CASE": {
|
|
28
|
+
"prefix": "morph-upper-snake",
|
|
29
|
+
"body": ["{{upperSnakeCase ${1:FEATURE_NAME}}}"],
|
|
30
|
+
"description": "Convert to UPPER_SNAKE_CASE (USER_AUTH)"
|
|
31
|
+
},
|
|
32
|
+
"MORPH Template - Conditional If": {
|
|
33
|
+
"prefix": "morph-if",
|
|
34
|
+
"body": [
|
|
35
|
+
"{{#if ${1:condition}}}",
|
|
36
|
+
" $2",
|
|
37
|
+
"{{/if}}"
|
|
38
|
+
],
|
|
39
|
+
"description": "Handlebars if block"
|
|
40
|
+
},
|
|
41
|
+
"MORPH Template - Conditional If-Else": {
|
|
42
|
+
"prefix": "morph-if-else",
|
|
43
|
+
"body": [
|
|
44
|
+
"{{#if ${1:condition}}}",
|
|
45
|
+
" $2",
|
|
46
|
+
"{{else}}",
|
|
47
|
+
" $3",
|
|
48
|
+
"{{/if}}"
|
|
49
|
+
],
|
|
50
|
+
"description": "Handlebars if-else block"
|
|
51
|
+
},
|
|
52
|
+
"MORPH Template - Each Loop": {
|
|
53
|
+
"prefix": "morph-each",
|
|
54
|
+
"body": [
|
|
55
|
+
"{{#each ${1:items}}}",
|
|
56
|
+
" {{this.$2}}",
|
|
57
|
+
"{{/each}}"
|
|
58
|
+
],
|
|
59
|
+
"description": "Handlebars each loop"
|
|
60
|
+
},
|
|
61
|
+
"MORPH Template - Unless": {
|
|
62
|
+
"prefix": "morph-unless",
|
|
63
|
+
"body": [
|
|
64
|
+
"{{#unless ${1:condition}}}",
|
|
65
|
+
" $2",
|
|
66
|
+
"{{/unless}}"
|
|
67
|
+
],
|
|
68
|
+
"description": "Handlebars unless block"
|
|
69
|
+
},
|
|
70
|
+
"MORPH Template - .NET Service Class": {
|
|
71
|
+
"prefix": "morph-service",
|
|
72
|
+
"body": [
|
|
73
|
+
"namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services;",
|
|
74
|
+
"",
|
|
75
|
+
"public class {{pascalCase FEATURE_NAME}}Service(",
|
|
76
|
+
" I{{pascalCase FEATURE_NAME}}Repository repository,",
|
|
77
|
+
" ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service",
|
|
78
|
+
"{",
|
|
79
|
+
" public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default)",
|
|
80
|
+
" {",
|
|
81
|
+
" $1",
|
|
82
|
+
" }",
|
|
83
|
+
"}"
|
|
84
|
+
],
|
|
85
|
+
"description": "MORPH .NET Service class template"
|
|
86
|
+
},
|
|
87
|
+
"MORPH Template - .NET Repository Class": {
|
|
88
|
+
"prefix": "morph-repository",
|
|
89
|
+
"body": [
|
|
90
|
+
"public class {{pascalCase FEATURE_NAME}}Repository(AppDbContext context) : I{{pascalCase FEATURE_NAME}}Repository",
|
|
91
|
+
"{",
|
|
92
|
+
" private readonly DbSet<{{pascalCase FEATURE_NAME}}> _dbSet = context.Set<{{pascalCase FEATURE_NAME}}>();",
|
|
93
|
+
"",
|
|
94
|
+
" public async Task<{{pascalCase FEATURE_NAME}}?> GetByIdAsync(int id, CancellationToken cancellationToken = default)",
|
|
95
|
+
" {",
|
|
96
|
+
" $1",
|
|
97
|
+
" }",
|
|
98
|
+
"}"
|
|
99
|
+
],
|
|
100
|
+
"description": "MORPH .NET Repository class template"
|
|
101
|
+
},
|
|
102
|
+
"MORPH Template - Blazor Component": {
|
|
103
|
+
"prefix": "morph-component",
|
|
104
|
+
"body": [
|
|
105
|
+
"@page \"/{{kebabCase FEATURE_NAME}}\"",
|
|
106
|
+
"@inject I{{pascalCase FEATURE_NAME}}Service {{pascalCase FEATURE_NAME}}Service",
|
|
107
|
+
"@inject ILogger<{{pascalCase FEATURE_NAME}}Page> Logger",
|
|
108
|
+
"",
|
|
109
|
+
"<PageTitle>{{pascalCase FEATURE_NAME}}</PageTitle>",
|
|
110
|
+
"",
|
|
111
|
+
"<div class=\"container-fluid\">",
|
|
112
|
+
" $1",
|
|
113
|
+
"</div>",
|
|
114
|
+
"",
|
|
115
|
+
"@code {",
|
|
116
|
+
" $2",
|
|
117
|
+
"}"
|
|
118
|
+
],
|
|
119
|
+
"description": "MORPH Blazor component template"
|
|
120
|
+
},
|
|
121
|
+
"MORPH Template - EF Core Migration": {
|
|
122
|
+
"prefix": "morph-migration",
|
|
123
|
+
"body": [
|
|
124
|
+
"public partial class Add{{pascalCase FEATURE_NAME}} : Migration",
|
|
125
|
+
"{",
|
|
126
|
+
" protected override void Up(MigrationBuilder migrationBuilder)",
|
|
127
|
+
" {",
|
|
128
|
+
" migrationBuilder.CreateTable(",
|
|
129
|
+
" name: \"{{pascalCase FEATURE_NAME}}s\",",
|
|
130
|
+
" columns: table => new",
|
|
131
|
+
" {",
|
|
132
|
+
" Id = table.Column<int>(type: \"int\", nullable: false)",
|
|
133
|
+
" .Annotation(\"SqlServer:Identity\", \"1, 1\"),",
|
|
134
|
+
" $1",
|
|
135
|
+
" },",
|
|
136
|
+
" constraints: table =>",
|
|
137
|
+
" {",
|
|
138
|
+
" table.PrimaryKey(\"PK_{{pascalCase FEATURE_NAME}}s\", x => x.Id);",
|
|
139
|
+
" });",
|
|
140
|
+
" }",
|
|
141
|
+
"",
|
|
142
|
+
" protected override void Down(MigrationBuilder migrationBuilder)",
|
|
143
|
+
" {",
|
|
144
|
+
" migrationBuilder.DropTable(name: \"{{pascalCase FEATURE_NAME}}s\");",
|
|
145
|
+
" }",
|
|
146
|
+
"}"
|
|
147
|
+
],
|
|
148
|
+
"description": "MORPH EF Core migration template"
|
|
149
|
+
},
|
|
150
|
+
"MORPH Template - Hangfire Job": {
|
|
151
|
+
"prefix": "morph-job",
|
|
152
|
+
"body": [
|
|
153
|
+
"public class {{pascalCase FEATURE_NAME}}ProcessorJob(",
|
|
154
|
+
" I{{pascalCase FEATURE_NAME}}Service service,",
|
|
155
|
+
" ILogger<{{pascalCase FEATURE_NAME}}ProcessorJob> logger) : I{{pascalCase FEATURE_NAME}}ProcessorJob",
|
|
156
|
+
"{",
|
|
157
|
+
" [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]",
|
|
158
|
+
" [Queue(\"default\")]",
|
|
159
|
+
" [JobDisplayName(\"{{pascalCase FEATURE_NAME}} Processing - ID: {0}\")]",
|
|
160
|
+
" public async Task ExecuteAsync(int id, CancellationToken cancellationToken)",
|
|
161
|
+
" {",
|
|
162
|
+
" $1",
|
|
163
|
+
" }",
|
|
164
|
+
"}"
|
|
165
|
+
],
|
|
166
|
+
"description": "MORPH Hangfire job template"
|
|
167
|
+
},
|
|
168
|
+
"MORPH Template - TypeScript Contract": {
|
|
169
|
+
"prefix": "morph-ts-contract",
|
|
170
|
+
"body": [
|
|
171
|
+
"export interface {{pascalCase FEATURE_NAME}} {",
|
|
172
|
+
" id: number;",
|
|
173
|
+
" $1",
|
|
174
|
+
"}",
|
|
175
|
+
"",
|
|
176
|
+
"export interface Create{{pascalCase FEATURE_NAME}}Request {",
|
|
177
|
+
" $2",
|
|
178
|
+
"}",
|
|
179
|
+
"",
|
|
180
|
+
"export interface Update{{pascalCase FEATURE_NAME}}Request {",
|
|
181
|
+
" $3",
|
|
182
|
+
"}"
|
|
183
|
+
],
|
|
184
|
+
"description": "MORPH TypeScript contract template"
|
|
185
|
+
}
|
|
186
|
+
}
|