@girardelli/architect-agents 8.1.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/dist/src/core/agent-generator/context-enricher.d.ts +17 -0
- package/dist/src/core/agent-generator/context-enricher.js +51 -0
- package/dist/src/core/agent-generator/context-enricher.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/base-detector.d.ts +8 -0
- package/dist/src/core/agent-generator/detectors/base-detector.js +12 -0
- package/dist/src/core/agent-generator/detectors/base-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/dart-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/dart-detector.js +16 -0
- package/dist/src/core/agent-generator/detectors/dart-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/framework-registry.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/framework-registry.js +81 -0
- package/dist/src/core/agent-generator/detectors/framework-registry.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/go-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/go-detector.js +25 -0
- package/dist/src/core/agent-generator/detectors/go-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/java-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/java-detector.js +44 -0
- package/dist/src/core/agent-generator/detectors/java-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/node-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/node-detector.js +28 -0
- package/dist/src/core/agent-generator/detectors/node-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/php-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/php-detector.js +28 -0
- package/dist/src/core/agent-generator/detectors/php-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/python-detector.d.ts +7 -0
- package/dist/src/core/agent-generator/detectors/python-detector.js +116 -0
- package/dist/src/core/agent-generator/detectors/python-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/ruby-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/ruby-detector.js +23 -0
- package/dist/src/core/agent-generator/detectors/ruby-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/rust-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/rust-detector.js +18 -0
- package/dist/src/core/agent-generator/detectors/rust-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/structure-detector.d.ts +4 -0
- package/dist/src/core/agent-generator/detectors/structure-detector.js +35 -0
- package/dist/src/core/agent-generator/detectors/structure-detector.js.map +1 -0
- package/dist/src/core/agent-generator/detectors/toolchain-detector.d.ts +5 -0
- package/dist/src/core/agent-generator/detectors/toolchain-detector.js +164 -0
- package/dist/src/core/agent-generator/detectors/toolchain-detector.js.map +1 -0
- package/dist/src/core/agent-generator/domain-inferrer.d.ts +51 -0
- package/dist/src/core/agent-generator/domain-inferrer.js +585 -0
- package/dist/src/core/agent-generator/domain-inferrer.js.map +1 -0
- package/dist/src/core/agent-generator/engines/audit-engine.d.ts +8 -0
- package/dist/src/core/agent-generator/engines/audit-engine.js +84 -0
- package/dist/src/core/agent-generator/engines/audit-engine.js.map +1 -0
- package/dist/src/core/agent-generator/engines/context-builder.d.ts +12 -0
- package/dist/src/core/agent-generator/engines/context-builder.js +84 -0
- package/dist/src/core/agent-generator/engines/context-builder.js.map +1 -0
- package/dist/src/core/agent-generator/engines/generation-engine.d.ts +7 -0
- package/dist/src/core/agent-generator/engines/generation-engine.js +160 -0
- package/dist/src/core/agent-generator/engines/generation-engine.js.map +1 -0
- package/dist/src/core/agent-generator/engines/generation-engine_deps.d.ts +21 -0
- package/dist/src/core/agent-generator/engines/generation-engine_deps.js +17 -0
- package/dist/src/core/agent-generator/engines/generation-engine_deps.js.map +1 -0
- package/dist/src/core/agent-generator/engines/suggestion-engine.d.ts +13 -0
- package/dist/src/core/agent-generator/engines/suggestion-engine.js +171 -0
- package/dist/src/core/agent-generator/engines/suggestion-engine.js.map +1 -0
- package/dist/src/core/agent-generator/engines/suggestion-engine_deps.d.ts +8 -0
- package/dist/src/core/agent-generator/engines/suggestion-engine_deps.js +5 -0
- package/dist/src/core/agent-generator/engines/suggestion-engine_deps.js.map +1 -0
- package/dist/src/core/agent-generator/enrichers/analysis-helpers.d.ts +9 -0
- package/dist/src/core/agent-generator/enrichers/analysis-helpers.js +51 -0
- package/dist/src/core/agent-generator/enrichers/analysis-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/enrichers/description-generator.d.ts +4 -0
- package/dist/src/core/agent-generator/enrichers/description-generator.js +82 -0
- package/dist/src/core/agent-generator/enrichers/description-generator.js.map +1 -0
- package/dist/src/core/agent-generator/enrichers/endpoint-extractor.d.ts +7 -0
- package/dist/src/core/agent-generator/enrichers/endpoint-extractor.js +90 -0
- package/dist/src/core/agent-generator/enrichers/endpoint-extractor.js.map +1 -0
- package/dist/src/core/agent-generator/enrichers/layer-classifier.d.ts +12 -0
- package/dist/src/core/agent-generator/enrichers/layer-classifier.js +152 -0
- package/dist/src/core/agent-generator/enrichers/layer-classifier.js.map +1 -0
- package/dist/src/core/agent-generator/enrichers/module-extractor.d.ts +10 -0
- package/dist/src/core/agent-generator/enrichers/module-extractor.js +173 -0
- package/dist/src/core/agent-generator/enrichers/module-extractor.js.map +1 -0
- package/dist/src/core/agent-generator/framework-detector.d.ts +17 -0
- package/dist/src/core/agent-generator/framework-detector.js +56 -0
- package/dist/src/core/agent-generator/framework-detector.js.map +1 -0
- package/dist/src/core/agent-generator/index.d.ts +25 -0
- package/dist/src/core/agent-generator/index.js +37 -0
- package/dist/src/core/agent-generator/index.js.map +1 -0
- package/dist/src/core/agent-generator/stack-detector.d.ts +13 -0
- package/dist/src/core/agent-generator/stack-detector.js +124 -0
- package/dist/src/core/agent-generator/stack-detector.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/agents.d.ts +9 -0
- package/dist/src/core/agent-generator/templates/core/agents.js +1127 -0
- package/dist/src/core/agent-generator/templates/core/agents.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/architecture-rules.d.ts +6 -0
- package/dist/src/core/agent-generator/templates/core/architecture-rules.js +275 -0
- package/dist/src/core/agent-generator/templates/core/architecture-rules.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/general-rules.d.ts +7 -0
- package/dist/src/core/agent-generator/templates/core/general-rules.js +301 -0
- package/dist/src/core/agent-generator/templates/core/general-rules.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/hooks-generator.d.ts +20 -0
- package/dist/src/core/agent-generator/templates/core/hooks-generator.js +235 -0
- package/dist/src/core/agent-generator/templates/core/hooks-generator.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/index-md.d.ts +6 -0
- package/dist/src/core/agent-generator/templates/core/index-md.js +247 -0
- package/dist/src/core/agent-generator/templates/core/index-md.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/orchestrator.d.ts +7 -0
- package/dist/src/core/agent-generator/templates/core/orchestrator.js +423 -0
- package/dist/src/core/agent-generator/templates/core/orchestrator.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/preflight.d.ts +7 -0
- package/dist/src/core/agent-generator/templates/core/preflight.js +213 -0
- package/dist/src/core/agent-generator/templates/core/preflight.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/quality-gates.d.ts +10 -0
- package/dist/src/core/agent-generator/templates/core/quality-gates.js +255 -0
- package/dist/src/core/agent-generator/templates/core/quality-gates.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/security-rules.d.ts +6 -0
- package/dist/src/core/agent-generator/templates/core/security-rules.js +529 -0
- package/dist/src/core/agent-generator/templates/core/security-rules.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/skills-generator.d.ts +18 -0
- package/dist/src/core/agent-generator/templates/core/skills-generator.js +547 -0
- package/dist/src/core/agent-generator/templates/core/skills-generator.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/workflow-fix-bug.d.ts +6 -0
- package/dist/src/core/agent-generator/templates/core/workflow-fix-bug.js +238 -0
- package/dist/src/core/agent-generator/templates/core/workflow-fix-bug.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/workflow-new-feature.d.ts +7 -0
- package/dist/src/core/agent-generator/templates/core/workflow-new-feature.js +321 -0
- package/dist/src/core/agent-generator/templates/core/workflow-new-feature.js.map +1 -0
- package/dist/src/core/agent-generator/templates/core/workflow-review.d.ts +6 -0
- package/dist/src/core/agent-generator/templates/core/workflow-review.js +105 -0
- package/dist/src/core/agent-generator/templates/core/workflow-review.js.map +1 -0
- package/dist/src/core/agent-generator/templates/domain/index.d.ts +21 -0
- package/dist/src/core/agent-generator/templates/domain/index.js +1179 -0
- package/dist/src/core/agent-generator/templates/domain/index.js.map +1 -0
- package/dist/src/core/agent-generator/templates/helpers/base-helpers.d.ts +10 -0
- package/dist/src/core/agent-generator/templates/helpers/base-helpers.js +20 -0
- package/dist/src/core/agent-generator/templates/helpers/base-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/templates/helpers/cross-ref-helpers.d.ts +2 -0
- package/dist/src/core/agent-generator/templates/helpers/cross-ref-helpers.js +77 -0
- package/dist/src/core/agent-generator/templates/helpers/cross-ref-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/templates/helpers/security-helpers.d.ts +2 -0
- package/dist/src/core/agent-generator/templates/helpers/security-helpers.js +182 -0
- package/dist/src/core/agent-generator/templates/helpers/security-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/templates/helpers/stack-helpers.d.ts +4 -0
- package/dist/src/core/agent-generator/templates/helpers/stack-helpers.js +69 -0
- package/dist/src/core/agent-generator/templates/helpers/stack-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/templates/helpers/structure-helpers.d.ts +2 -0
- package/dist/src/core/agent-generator/templates/helpers/structure-helpers.js +275 -0
- package/dist/src/core/agent-generator/templates/helpers/structure-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/templates/helpers/summary-helpers.d.ts +6 -0
- package/dist/src/core/agent-generator/templates/helpers/summary-helpers.js +56 -0
- package/dist/src/core/agent-generator/templates/helpers/summary-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/templates/stack/index.d.ts +7 -0
- package/dist/src/core/agent-generator/templates/stack/index.js +695 -0
- package/dist/src/core/agent-generator/templates/stack/index.js.map +1 -0
- package/dist/src/core/agent-generator/templates/template-helpers.d.ts +11 -0
- package/dist/src/core/agent-generator/templates/template-helpers.js +12 -0
- package/dist/src/core/agent-generator/templates/template-helpers.js.map +1 -0
- package/dist/src/core/agent-generator/types/agent.d.ts +39 -0
- package/dist/src/core/agent-generator/types/agent.js +27 -0
- package/dist/src/core/agent-generator/types/agent.js.map +1 -0
- package/dist/src/core/agent-generator/types/domain.d.ts +58 -0
- package/dist/src/core/agent-generator/types/domain.js +2 -0
- package/dist/src/core/agent-generator/types/domain.js.map +1 -0
- package/dist/src/core/agent-generator/types/stack.d.ts +36 -0
- package/dist/src/core/agent-generator/types/stack.js +2 -0
- package/dist/src/core/agent-generator/types/stack.js.map +1 -0
- package/dist/src/core/agent-generator/types/template.d.ts +29 -0
- package/dist/src/core/agent-generator/types/template.js +2 -0
- package/dist/src/core/agent-generator/types/template.js.map +1 -0
- package/dist/src/core/agent-runtime/ai-provider.d.ts +33 -0
- package/dist/src/core/agent-runtime/ai-provider.js +146 -0
- package/dist/src/core/agent-runtime/ai-provider.js.map +1 -0
- package/dist/src/core/agent-runtime/executor.d.ts +13 -0
- package/dist/src/core/agent-runtime/executor.js +138 -0
- package/dist/src/core/agent-runtime/executor.js.map +1 -0
- package/dist/src/core/agent-runtime/human-gate.d.ts +16 -0
- package/dist/src/core/agent-runtime/human-gate.js +70 -0
- package/dist/src/core/agent-runtime/human-gate.js.map +1 -0
- package/dist/tests/agent-generator.test.d.ts +1 -0
- package/dist/tests/agent-generator.test.js +349 -0
- package/dist/tests/agent-generator.test.js.map +1 -0
- package/dist/tests/agent-runtime.test.d.ts +1 -0
- package/dist/tests/agent-runtime.test.js +107 -0
- package/dist/tests/agent-runtime.test.js.map +1 -0
- package/dist/tests/context-enricher.test.d.ts +1 -0
- package/dist/tests/context-enricher.test.js +875 -0
- package/dist/tests/context-enricher.test.js.map +1 -0
- package/dist/tests/framework-detector.test.d.ts +1 -0
- package/dist/tests/framework-detector.test.js +882 -0
- package/dist/tests/framework-detector.test.js.map +1 -0
- package/dist/tests/stack-detector.test.d.ts +1 -0
- package/dist/tests/stack-detector.test.js +183 -0
- package/dist/tests/stack-detector.test.js.map +1 -0
- package/dist/tests/template-generation.test.d.ts +1 -0
- package/dist/tests/template-generation.test.js +571 -0
- package/dist/tests/template-generation.test.js.map +1 -0
- package/dist/tests/template-helpers.test.d.ts +1 -0
- package/dist/tests/template-helpers.test.js +967 -0
- package/dist/tests/template-helpers.test.js.map +1 -0
- package/package.json +24 -0
- package/src/core/agent-generator/context-enricher.ts +67 -0
- package/src/core/agent-generator/detectors/base-detector.ts +18 -0
- package/src/core/agent-generator/detectors/dart-detector.ts +17 -0
- package/src/core/agent-generator/detectors/framework-registry.ts +82 -0
- package/src/core/agent-generator/detectors/go-detector.ts +26 -0
- package/src/core/agent-generator/detectors/java-detector.ts +46 -0
- package/src/core/agent-generator/detectors/node-detector.ts +28 -0
- package/src/core/agent-generator/detectors/php-detector.ts +28 -0
- package/src/core/agent-generator/detectors/python-detector.ts +125 -0
- package/src/core/agent-generator/detectors/ruby-detector.ts +24 -0
- package/src/core/agent-generator/detectors/rust-detector.ts +19 -0
- package/src/core/agent-generator/detectors/structure-detector.ts +38 -0
- package/src/core/agent-generator/detectors/toolchain-detector.ts +181 -0
- package/src/core/agent-generator/domain-inferrer.ts +630 -0
- package/src/core/agent-generator/engines/audit-engine.ts +98 -0
- package/src/core/agent-generator/engines/context-builder.ts +96 -0
- package/src/core/agent-generator/engines/generation-engine.ts +184 -0
- package/src/core/agent-generator/engines/generation-engine_deps.ts +21 -0
- package/src/core/agent-generator/engines/suggestion-engine.ts +202 -0
- package/src/core/agent-generator/engines/suggestion-engine_deps.ts +8 -0
- package/src/core/agent-generator/enrichers/analysis-helpers.ts +58 -0
- package/src/core/agent-generator/enrichers/description-generator.ts +91 -0
- package/src/core/agent-generator/enrichers/endpoint-extractor.ts +114 -0
- package/src/core/agent-generator/enrichers/layer-classifier.ts +156 -0
- package/src/core/agent-generator/enrichers/module-extractor.ts +203 -0
- package/src/core/agent-generator/framework-detector.ts +66 -0
- package/src/core/agent-generator/index.ts +55 -0
- package/src/core/agent-generator/stack-detector.ts +115 -0
- package/src/core/agent-generator/templates/core/agents.ts +1168 -0
- package/src/core/agent-generator/templates/core/architecture-rules.ts +288 -0
- package/src/core/agent-generator/templates/core/general-rules.ts +306 -0
- package/src/core/agent-generator/templates/core/hooks-generator.ts +244 -0
- package/src/core/agent-generator/templates/core/index-md.ts +261 -0
- package/src/core/agent-generator/templates/core/orchestrator.ts +462 -0
- package/src/core/agent-generator/templates/core/preflight.ts +216 -0
- package/src/core/agent-generator/templates/core/quality-gates.ts +257 -0
- package/src/core/agent-generator/templates/core/security-rules.ts +544 -0
- package/src/core/agent-generator/templates/core/skills-generator.ts +586 -0
- package/src/core/agent-generator/templates/core/workflow-fix-bug.ts +240 -0
- package/src/core/agent-generator/templates/core/workflow-new-feature.ts +323 -0
- package/src/core/agent-generator/templates/core/workflow-review.ts +107 -0
- package/src/core/agent-generator/templates/domain/index.ts +1204 -0
- package/src/core/agent-generator/templates/helpers/base-helpers.ts +33 -0
- package/src/core/agent-generator/templates/helpers/cross-ref-helpers.ts +79 -0
- package/src/core/agent-generator/templates/helpers/security-helpers.ts +198 -0
- package/src/core/agent-generator/templates/helpers/stack-helpers.ts +80 -0
- package/src/core/agent-generator/templates/helpers/structure-helpers.ts +293 -0
- package/src/core/agent-generator/templates/helpers/summary-helpers.ts +67 -0
- package/src/core/agent-generator/templates/stack/index.ts +705 -0
- package/src/core/agent-generator/templates/template-helpers.ts +12 -0
- package/src/core/agent-generator/types/agent.ts +65 -0
- package/src/core/agent-generator/types/domain.ts +63 -0
- package/src/core/agent-generator/types/stack.ts +38 -0
- package/src/core/agent-generator/types/template.ts +31 -0
- package/src/core/agent-runtime/ai-provider.ts +178 -0
- package/src/core/agent-runtime/executor.ts +148 -0
- package/src/core/agent-runtime/human-gate.ts +69 -0
- package/tests/agent-generator.test.ts +428 -0
- package/tests/agent-runtime.test.ts +125 -0
- package/tests/context-enricher.test.ts +972 -0
- package/tests/framework-detector.test.ts +1172 -0
- package/tests/stack-detector.test.ts +241 -0
- package/tests/template-generation.test.ts +709 -0
- package/tests/template-helpers.test.ts +1130 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,1179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain templates — C4, BDD, TDD, ADR, Threat Model.
|
|
3
|
+
* Reutilizable templates referenced by workflows and agents.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Helper to safely extract enriched context
|
|
7
|
+
*/
|
|
8
|
+
function getEnriched(ctx) {
|
|
9
|
+
if (ctx && 'domain' in ctx) {
|
|
10
|
+
return ctx;
|
|
11
|
+
}
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate C4 Architecture template with optional pre-filled examples
|
|
16
|
+
*/
|
|
17
|
+
export function generateC4Template(ctx) {
|
|
18
|
+
const enriched = getEnriched(ctx);
|
|
19
|
+
const domain = enriched.domain;
|
|
20
|
+
const modules = enriched.modules || [];
|
|
21
|
+
const endpoints = enriched.endpoints || [];
|
|
22
|
+
const integrations = domain?.integrations || [];
|
|
23
|
+
// Build Level 1 content
|
|
24
|
+
let level1Content = `Atores:
|
|
25
|
+
- [ator 1]: [descrição do papel]
|
|
26
|
+
- [ator 2]: [descrição do papel]`;
|
|
27
|
+
if (domain?.businessEntities && domain.businessEntities.length > 0) {
|
|
28
|
+
const actorsFromEntities = domain.businessEntities.slice(0, 2).map((e) => `- ${e.name}: Entidade de negócio`).join('\n');
|
|
29
|
+
level1Content = `Atores:\n${actorsFromEntities}`;
|
|
30
|
+
}
|
|
31
|
+
let externalSystems = `- [sistema 1]: [como interage]
|
|
32
|
+
- [sistema 2]: [como interage]`;
|
|
33
|
+
if (integrations.length > 0) {
|
|
34
|
+
externalSystems = integrations
|
|
35
|
+
.map((i) => `- ${i.name} (${i.type}): Integração de negócio`)
|
|
36
|
+
.join('\n');
|
|
37
|
+
}
|
|
38
|
+
// Build Level 2 content
|
|
39
|
+
let containerDiagram = `┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
40
|
+
│ Frontend │───▶│ Backend │───▶│ Database │
|
|
41
|
+
│ (Web/App) │ │ (API) │ │ (PostgreSQL)│
|
|
42
|
+
└──────────────┘ └──────────────┘ └──────────────┘
|
|
43
|
+
│
|
|
44
|
+
▼
|
|
45
|
+
┌──────────────┐
|
|
46
|
+
│ External │
|
|
47
|
+
│ Service │
|
|
48
|
+
└──────────────┘`;
|
|
49
|
+
if (ctx && 'stack' in ctx && ctx.stack) {
|
|
50
|
+
const stack = ctx.stack;
|
|
51
|
+
let frontendLabel = stack.hasFrontend ? `${stack.frameworks[0] || 'Frontend'}` : '[Frontend]';
|
|
52
|
+
let backendLabel = stack.frameworks[1] || 'Backend';
|
|
53
|
+
let dbLabel = '[Database]';
|
|
54
|
+
if (stack.hasDatabase) {
|
|
55
|
+
dbLabel = stack.frameworks.includes('PostgreSQL') ? 'PostgreSQL' : 'Database';
|
|
56
|
+
}
|
|
57
|
+
containerDiagram = `┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
58
|
+
│ ${frontendLabel.padEnd(11)}│───▶│ ${backendLabel.padEnd(11)}│───▶│ ${dbLabel.padEnd(11)}│
|
|
59
|
+
└──────────────┘ └──────────────┘ └──────────────┘`;
|
|
60
|
+
}
|
|
61
|
+
// Build Level 3 content
|
|
62
|
+
let componentContent = `Módulo: [nome]
|
|
63
|
+
├── Controller: [nome] — [responsabilidade]
|
|
64
|
+
├── Service: [nome] — [responsabilidade]
|
|
65
|
+
├── Entity: [nome] — [campos principais]
|
|
66
|
+
├── DTO: [nome] — [campos de request/response]
|
|
67
|
+
└── Tests: [lista de testes]`;
|
|
68
|
+
if (modules.length > 0) {
|
|
69
|
+
const firstModule = modules[0];
|
|
70
|
+
const controllers = firstModule.controllers.slice(0, 2).join(', ') || '[Controllers]';
|
|
71
|
+
const services = firstModule.services.slice(0, 2).join(', ') || '[Services]';
|
|
72
|
+
const entities = firstModule.entities.slice(0, 1).join(', ') || '[Entities]';
|
|
73
|
+
componentContent = `Módulo: ${firstModule.name}
|
|
74
|
+
├── Controller: ${controllers} — Expõe endpoints REST
|
|
75
|
+
├── Service: ${services} — Lógica de negócio
|
|
76
|
+
├── Entity: ${entities} — Persistência de dados
|
|
77
|
+
├── DTO: [Request/Response]
|
|
78
|
+
└── Tests: ${firstModule.testFiles.length > 0 ? 'Implementados' : 'Pendentes'}`;
|
|
79
|
+
}
|
|
80
|
+
// Build Level 4 content — framework-aware code example
|
|
81
|
+
const stack = ctx && 'stack' in ctx ? ctx.stack : undefined;
|
|
82
|
+
const langs = stack ? stack.languages.map((l) => l.toLowerCase()) : [];
|
|
83
|
+
const isPython = langs.includes('python');
|
|
84
|
+
const isDart = langs.includes('dart');
|
|
85
|
+
const isJava = langs.includes('java') || langs.includes('kotlin');
|
|
86
|
+
const isGo = langs.includes('go');
|
|
87
|
+
const isRust = langs.includes('rust');
|
|
88
|
+
let level4Lang = 'typescript';
|
|
89
|
+
let level4Content = `interface IExemploService {
|
|
90
|
+
metodo(param: Tipo): Promise<Retorno>;
|
|
91
|
+
}`;
|
|
92
|
+
if (isPython) {
|
|
93
|
+
level4Lang = 'python';
|
|
94
|
+
level4Content = `class IExemploService(ABC):
|
|
95
|
+
@abstractmethod
|
|
96
|
+
async def metodo(self, param: Tipo) -> Retorno:
|
|
97
|
+
...`;
|
|
98
|
+
}
|
|
99
|
+
else if (isDart) {
|
|
100
|
+
level4Lang = 'dart';
|
|
101
|
+
level4Content = `abstract class IExemploService {
|
|
102
|
+
Future<Retorno> metodo(Tipo param);
|
|
103
|
+
}`;
|
|
104
|
+
}
|
|
105
|
+
else if (isJava) {
|
|
106
|
+
level4Lang = 'java';
|
|
107
|
+
level4Content = `public interface IExemploService {
|
|
108
|
+
Retorno metodo(Tipo param);
|
|
109
|
+
}`;
|
|
110
|
+
}
|
|
111
|
+
else if (isGo) {
|
|
112
|
+
level4Lang = 'go';
|
|
113
|
+
level4Content = `type ExemploService interface {
|
|
114
|
+
Metodo(param Tipo) (Retorno, error)
|
|
115
|
+
}`;
|
|
116
|
+
}
|
|
117
|
+
else if (isRust) {
|
|
118
|
+
level4Lang = 'rust';
|
|
119
|
+
level4Content = `trait ExemploService {
|
|
120
|
+
fn metodo(&self, param: Tipo) -> Result<Retorno, Error>;
|
|
121
|
+
}`;
|
|
122
|
+
}
|
|
123
|
+
if (endpoints.length > 0) {
|
|
124
|
+
const firstEndpoint = endpoints[0];
|
|
125
|
+
if (isPython) {
|
|
126
|
+
level4Content = `class I${firstEndpoint.handler}Service(ABC):
|
|
127
|
+
# ${firstEndpoint.method} ${firstEndpoint.path}
|
|
128
|
+
@abstractmethod
|
|
129
|
+
async def handle_${firstEndpoint.handler.toLowerCase()}(self, req: Request) -> Response:
|
|
130
|
+
...`;
|
|
131
|
+
}
|
|
132
|
+
else if (isDart) {
|
|
133
|
+
level4Content = `abstract class I${firstEndpoint.handler}Service {
|
|
134
|
+
// ${firstEndpoint.method} ${firstEndpoint.path}
|
|
135
|
+
Future<Response> handle${firstEndpoint.handler}(Request req);
|
|
136
|
+
}`;
|
|
137
|
+
}
|
|
138
|
+
else if (isJava) {
|
|
139
|
+
level4Content = `public interface I${firstEndpoint.handler}Service {
|
|
140
|
+
// ${firstEndpoint.method} ${firstEndpoint.path}
|
|
141
|
+
Response handle${firstEndpoint.handler}(Request req);
|
|
142
|
+
}`;
|
|
143
|
+
}
|
|
144
|
+
else if (isGo) {
|
|
145
|
+
level4Content = `type ${firstEndpoint.handler}Service interface {
|
|
146
|
+
// ${firstEndpoint.method} ${firstEndpoint.path}
|
|
147
|
+
Handle${firstEndpoint.handler}(req *Request) (*Response, error)
|
|
148
|
+
}`;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
level4Content = `interface I${firstEndpoint.handler}Service {
|
|
152
|
+
// ${firstEndpoint.method} ${firstEndpoint.path}
|
|
153
|
+
handle${firstEndpoint.handler}(req: Request): Promise<Response>;
|
|
154
|
+
}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return `# 🏗️ Template: Arquitetura C4
|
|
158
|
+
|
|
159
|
+
> Preencher os 4 níveis relevantes para a feature/mudança.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Nível 1 — Contexto
|
|
164
|
+
|
|
165
|
+
> Visão de pássaro: quem são os atores e sistemas envolvidos?
|
|
166
|
+
|
|
167
|
+
\`\`\`
|
|
168
|
+
${level1Content}
|
|
169
|
+
|
|
170
|
+
Sistemas Externos:
|
|
171
|
+
${externalSystems}
|
|
172
|
+
|
|
173
|
+
Fluxo de dados:
|
|
174
|
+
[ator] → [sistema] → [nosso sistema] → [resposta]
|
|
175
|
+
\`\`\`
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Nível 2 — Container
|
|
180
|
+
|
|
181
|
+
> Quais serviços, apps, bancos de dados são tocados?
|
|
182
|
+
|
|
183
|
+
\`\`\`
|
|
184
|
+
${containerDiagram}
|
|
185
|
+
\`\`\`
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Nível 3 — Componente
|
|
190
|
+
|
|
191
|
+
> Quais módulos, classes, serviços são criados ou modificados?
|
|
192
|
+
|
|
193
|
+
\`\`\`
|
|
194
|
+
${componentContent}
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Nível 4 — Código (se complexo)
|
|
200
|
+
|
|
201
|
+
> Interfaces, tipos, contratos. Apenas para decisões complexas.
|
|
202
|
+
|
|
203
|
+
\`\`\`${level4Lang}
|
|
204
|
+
${level4Content}
|
|
205
|
+
\`\`\`
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Decisões Arquiteturais
|
|
210
|
+
|
|
211
|
+
Se a decisão é significativa → criar ADR separado (ver template ADR).
|
|
212
|
+
`;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Generate BDD template with optional pre-filled examples from project domain
|
|
216
|
+
*/
|
|
217
|
+
export function generateBddTemplate(ctx) {
|
|
218
|
+
const enriched = getEnriched(ctx);
|
|
219
|
+
const domain = enriched.domain;
|
|
220
|
+
const businessEntities = domain?.businessEntities || [];
|
|
221
|
+
const compliance = domain?.compliance || [];
|
|
222
|
+
let featureName = '[Nome da Feature]';
|
|
223
|
+
let exampleScenario = ` Scenario: [cenário principal - sucesso]
|
|
224
|
+
Given [contexto / pré-condição]
|
|
225
|
+
And [contexto adicional, se necessário]
|
|
226
|
+
When [ação do usuário]
|
|
227
|
+
Then [resultado esperado]
|
|
228
|
+
And [efeito colateral, se houver]`;
|
|
229
|
+
// Generate example scenario from first business entity
|
|
230
|
+
if (businessEntities.length > 0) {
|
|
231
|
+
const entity = businessEntities[0];
|
|
232
|
+
featureName = `Criar e Validar ${entity.name}`;
|
|
233
|
+
exampleScenario = ` Scenario: Criar ${entity.name} com sucesso
|
|
234
|
+
Given o usuário está no formulário de criação de ${entity.name}
|
|
235
|
+
And todos os campos obrigatórios estão vazios
|
|
236
|
+
When o usuário preenche os campos corretamente
|
|
237
|
+
Then a entidade é persistida no banco de dados
|
|
238
|
+
And uma mensagem de sucesso é exibida ao usuário`;
|
|
239
|
+
}
|
|
240
|
+
let complianceScenario = ` Scenario: [cenário de acesso negado]
|
|
241
|
+
Given [usuário sem permissão]
|
|
242
|
+
When [tenta acessar recurso]
|
|
243
|
+
Then [resposta 403 / redirect]`;
|
|
244
|
+
// Add compliance-specific scenario if applicable
|
|
245
|
+
if (compliance.length > 0) {
|
|
246
|
+
const comp = compliance[0];
|
|
247
|
+
if (comp.name === 'LGPD') {
|
|
248
|
+
complianceScenario = ` Scenario: Usuário solicita exclusão de dados sob LGPD
|
|
249
|
+
Given um usuário autenticado no sistema
|
|
250
|
+
When o usuário solicita a exclusão de seus dados pessoais
|
|
251
|
+
Then todos os seus registros são anonimizados ou removidos
|
|
252
|
+
And a solicitação é registrada em audit log`;
|
|
253
|
+
}
|
|
254
|
+
else if (comp.name === 'HIPAA') {
|
|
255
|
+
complianceScenario = ` Scenario: Acesso a dados sensíveis de paciente
|
|
256
|
+
Given um profissional de saúde autenticado
|
|
257
|
+
When o profissional acessa registros de paciente
|
|
258
|
+
Then o acesso é registrado com timestamp e usuário
|
|
259
|
+
And a ação é auditada para compliance`;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
let domainSpecificSection = '';
|
|
263
|
+
if (domain?.domain === 'fintech' || domain?.domain === 'payment') {
|
|
264
|
+
domainSpecificSection = `
|
|
265
|
+
|
|
266
|
+
# ── Cenários de Fintech ──
|
|
267
|
+
|
|
268
|
+
Scenario: Transação com saldo insuficiente
|
|
269
|
+
Given a conta do usuário tem saldo de R$ 50
|
|
270
|
+
When o usuário tenta transferir R$ 100
|
|
271
|
+
Then a transação é rejeitada
|
|
272
|
+
And a mensagem "Saldo insuficiente" é exibida
|
|
273
|
+
|
|
274
|
+
Scenario: Taxa de câmbio atualizada
|
|
275
|
+
Given uma transação internacional está pendente
|
|
276
|
+
When a taxa de câmbio muda de 5.0 para 5.2
|
|
277
|
+
Then o valor em reais é recalculado
|
|
278
|
+
And o usuário é notificado da mudança`;
|
|
279
|
+
}
|
|
280
|
+
else if (domain?.domain === 'tax' || domain?.subDomain === 'tax-processing') {
|
|
281
|
+
domainSpecificSection = `
|
|
282
|
+
|
|
283
|
+
# ── Cenários de Impostos ──
|
|
284
|
+
|
|
285
|
+
Scenario: Calcular imposto sobre renda
|
|
286
|
+
Given um contribuinte com rendimento mensal de R$ 5000
|
|
287
|
+
When o sistema calcula o imposto devido
|
|
288
|
+
Then o valor segue a tabela progressiva do IRPF
|
|
289
|
+
And o resultado é salvo no banco de dados
|
|
290
|
+
|
|
291
|
+
Scenario: Gerar DARF automático
|
|
292
|
+
Given uma obrigação de imposto vencendo amanhã
|
|
293
|
+
When o sistema processa as obrigações pendentes
|
|
294
|
+
Then um DARF é gerado automaticamente
|
|
295
|
+
And uma notificação é enviada ao contribuinte`;
|
|
296
|
+
}
|
|
297
|
+
return `# 🧪 Template: BDD — Behavior-Driven Development
|
|
298
|
+
|
|
299
|
+
> Um cenário para cada critério de aceite + cenários de erro + edge cases.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Feature: ${featureName}
|
|
304
|
+
|
|
305
|
+
\`\`\`gherkin
|
|
306
|
+
Feature: ${featureName}
|
|
307
|
+
Como usuário,
|
|
308
|
+
Quero interagir com ${businessEntities[0]?.name || 'o sistema'},
|
|
309
|
+
Para alcançar meu objetivo de negócio.
|
|
310
|
+
|
|
311
|
+
# ── Happy Path ──
|
|
312
|
+
|
|
313
|
+
${exampleScenario}
|
|
314
|
+
|
|
315
|
+
# ── Validações ──
|
|
316
|
+
|
|
317
|
+
Scenario: Validação de dados obrigatórios
|
|
318
|
+
Given um formulário de criação
|
|
319
|
+
When o usuário tenta enviar sem preencher campos obrigatórios
|
|
320
|
+
Then mensagens de erro são exibidas para cada campo
|
|
321
|
+
|
|
322
|
+
# ── Edge Cases ──
|
|
323
|
+
|
|
324
|
+
Scenario: Lidar com valores boundary
|
|
325
|
+
Given o sistema aceita valores de 0 a 999999.99
|
|
326
|
+
When o usuário tenta inserir um valor fora do range
|
|
327
|
+
Then um erro de validação é retornado
|
|
328
|
+
|
|
329
|
+
# ── Permissões ──
|
|
330
|
+
|
|
331
|
+
${complianceScenario}${domainSpecificSection}
|
|
332
|
+
\`\`\`
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Checklist
|
|
337
|
+
|
|
338
|
+
\`\`\`
|
|
339
|
+
□ Cada critério de aceite tem ≥ 1 cenário
|
|
340
|
+
□ Happy path coberto
|
|
341
|
+
□ Error paths cobertos
|
|
342
|
+
□ Edge cases cobertos
|
|
343
|
+
□ Permissões/autenticação cobertos
|
|
344
|
+
□ Cenários são independentes entre si
|
|
345
|
+
\`\`\`
|
|
346
|
+
`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Generate TDD template with optional pre-filled examples
|
|
350
|
+
*/
|
|
351
|
+
export function generateTddTemplate(ctx) {
|
|
352
|
+
const enriched = getEnriched(ctx);
|
|
353
|
+
const modules = enriched.modules || [];
|
|
354
|
+
const endpoints = enriched.endpoints || [];
|
|
355
|
+
const stack = ctx && 'stack' in ctx ? ctx.stack : undefined;
|
|
356
|
+
let testFramework = 'jest';
|
|
357
|
+
let exampleTest = ` it('should [resultado esperado] when [condição]', () => {
|
|
358
|
+
// Arrange
|
|
359
|
+
const input = ...;
|
|
360
|
+
|
|
361
|
+
// Act
|
|
362
|
+
const result = metodo(input);
|
|
363
|
+
|
|
364
|
+
// Assert
|
|
365
|
+
expect(result).toEqual(expected);
|
|
366
|
+
});`;
|
|
367
|
+
// Detect test framework from stack (case-insensitive comparison)
|
|
368
|
+
if (stack) {
|
|
369
|
+
const langs = stack.languages.map((l) => l.toLowerCase());
|
|
370
|
+
if (langs.includes('python')) {
|
|
371
|
+
testFramework = 'pytest';
|
|
372
|
+
}
|
|
373
|
+
else if (langs.includes('dart')) {
|
|
374
|
+
testFramework = 'flutter_test';
|
|
375
|
+
}
|
|
376
|
+
else if (langs.includes('java') || langs.includes('kotlin')) {
|
|
377
|
+
testFramework = 'junit';
|
|
378
|
+
}
|
|
379
|
+
else if (langs.includes('go')) {
|
|
380
|
+
testFramework = 'go_test';
|
|
381
|
+
}
|
|
382
|
+
else if (langs.includes('rust')) {
|
|
383
|
+
testFramework = 'cargo_test';
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
let moduleName = '[Nome do Módulo/Classe]';
|
|
387
|
+
let methodName = '[método/função]';
|
|
388
|
+
let exampleModule = modules[0];
|
|
389
|
+
if (exampleModule) {
|
|
390
|
+
moduleName = exampleModule.name;
|
|
391
|
+
const service = exampleModule.services[0] || 'Service';
|
|
392
|
+
methodName = `create${service.replace('Service', '')}`;
|
|
393
|
+
if (testFramework === 'pytest') {
|
|
394
|
+
exampleTest = ` def test_should_create_entity_successfully(self):
|
|
395
|
+
# Arrange
|
|
396
|
+
input_data = {"name": "Test Entity"}
|
|
397
|
+
|
|
398
|
+
# Act
|
|
399
|
+
result = service.${methodName}(input_data)
|
|
400
|
+
|
|
401
|
+
# Assert
|
|
402
|
+
assert result is not None
|
|
403
|
+
assert result.name == "Test Entity"`;
|
|
404
|
+
}
|
|
405
|
+
else if (testFramework === 'flutter_test') {
|
|
406
|
+
exampleTest = ` test('should create entity successfully', () async {
|
|
407
|
+
// Arrange
|
|
408
|
+
final input = CreateRequest(name: 'Test');
|
|
409
|
+
|
|
410
|
+
// Act
|
|
411
|
+
final result = await service.${methodName}(input);
|
|
412
|
+
|
|
413
|
+
// Assert
|
|
414
|
+
expect(result, isNotNull);
|
|
415
|
+
expect(result.name, equals('Test'));
|
|
416
|
+
});`;
|
|
417
|
+
}
|
|
418
|
+
else if (testFramework === 'go_test') {
|
|
419
|
+
exampleTest = `func TestShould_Create${moduleName}_Successfully(t *testing.T) {
|
|
420
|
+
// Arrange
|
|
421
|
+
input := ${moduleName}Input{Name: "Test Entity"}
|
|
422
|
+
|
|
423
|
+
// Act
|
|
424
|
+
result, err := service.${methodName}(input)
|
|
425
|
+
|
|
426
|
+
// Assert
|
|
427
|
+
assert.NoError(t, err)
|
|
428
|
+
assert.NotNil(t, result)
|
|
429
|
+
assert.Equal(t, "Test Entity", result.Name)
|
|
430
|
+
}`;
|
|
431
|
+
}
|
|
432
|
+
else if (testFramework === 'cargo_test') {
|
|
433
|
+
exampleTest = `#[test]
|
|
434
|
+
fn test_should_create_${moduleName.toLowerCase()}_successfully() {
|
|
435
|
+
// Arrange
|
|
436
|
+
let input = ${moduleName}Input { name: "Test Entity".to_string() };
|
|
437
|
+
|
|
438
|
+
// Act
|
|
439
|
+
let result = service.${methodName}(&input);
|
|
440
|
+
|
|
441
|
+
// Assert
|
|
442
|
+
assert!(result.is_ok());
|
|
443
|
+
assert_eq!(result.unwrap().name, "Test Entity");
|
|
444
|
+
}`;
|
|
445
|
+
}
|
|
446
|
+
else if (testFramework === 'junit') {
|
|
447
|
+
exampleTest = ` @Test
|
|
448
|
+
void shouldCreate${moduleName}Successfully() {
|
|
449
|
+
// Arrange
|
|
450
|
+
var input = new ${moduleName}Input("Test Entity");
|
|
451
|
+
|
|
452
|
+
// Act
|
|
453
|
+
var result = service.${methodName}(input);
|
|
454
|
+
|
|
455
|
+
// Assert
|
|
456
|
+
assertNotNull(result);
|
|
457
|
+
assertEquals("Test Entity", result.getName());
|
|
458
|
+
}`;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
exampleTest = ` it('should create ${moduleName} successfully', () => {
|
|
462
|
+
// Arrange
|
|
463
|
+
const input = { name: 'Test Entity' };
|
|
464
|
+
|
|
465
|
+
// Act
|
|
466
|
+
const result = service.${methodName}(input);
|
|
467
|
+
|
|
468
|
+
// Assert
|
|
469
|
+
expect(result).toBeDefined();
|
|
470
|
+
expect(result.name).toBe('Test Entity');
|
|
471
|
+
});`;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
let endpointTest = '';
|
|
475
|
+
if (endpoints.length > 0) {
|
|
476
|
+
const endpoint = endpoints[0];
|
|
477
|
+
if (testFramework === 'pytest') {
|
|
478
|
+
endpointTest = `
|
|
479
|
+
|
|
480
|
+
def test_${endpoint.method.toLowerCase()}_${endpoint.path.replace(/\//g, '_')}():
|
|
481
|
+
"""Test ${endpoint.method} ${endpoint.path}"""
|
|
482
|
+
# Arrange
|
|
483
|
+
client = TestClient(app)
|
|
484
|
+
|
|
485
|
+
# Act
|
|
486
|
+
response = client.${endpoint.method.toLowerCase()}("${endpoint.path}")
|
|
487
|
+
|
|
488
|
+
# Assert
|
|
489
|
+
assert response.status_code == 200`;
|
|
490
|
+
}
|
|
491
|
+
else if (testFramework === 'flutter_test') {
|
|
492
|
+
endpointTest = `
|
|
493
|
+
|
|
494
|
+
testWidgets('should ${endpoint.method.toLowerCase()} ${endpoint.path} correctly', (WidgetTester tester) async {
|
|
495
|
+
// Arrange
|
|
496
|
+
when(mockClient.${endpoint.method.toLowerCase()}(endpoint)).thenAnswer((_) async => Response('{}', 200));
|
|
497
|
+
|
|
498
|
+
// Act
|
|
499
|
+
final result = await client.${endpoint.method.toLowerCase()}(endpoint);
|
|
500
|
+
|
|
501
|
+
// Assert
|
|
502
|
+
expect(result.statusCode, equals(200));
|
|
503
|
+
});`;
|
|
504
|
+
}
|
|
505
|
+
else if (testFramework === 'go_test') {
|
|
506
|
+
endpointTest = `
|
|
507
|
+
|
|
508
|
+
func Test_${endpoint.method}_${endpoint.path.replace(/\//g, '_')}(t *testing.T) {
|
|
509
|
+
// Arrange
|
|
510
|
+
req := httptest.NewRequest("${endpoint.method}", "${endpoint.path}", nil)
|
|
511
|
+
w := httptest.NewRecorder()
|
|
512
|
+
|
|
513
|
+
// Act
|
|
514
|
+
handler.ServeHTTP(w, req)
|
|
515
|
+
|
|
516
|
+
// Assert
|
|
517
|
+
assert.Equal(t, http.StatusOK, w.Code)
|
|
518
|
+
}`;
|
|
519
|
+
}
|
|
520
|
+
else if (testFramework === 'cargo_test') {
|
|
521
|
+
endpointTest = `
|
|
522
|
+
|
|
523
|
+
#[actix_web::test]
|
|
524
|
+
async fn test_${endpoint.method.toLowerCase()}_${endpoint.path.replace(/\//g, '_')}() {
|
|
525
|
+
// Arrange
|
|
526
|
+
let app = test::init_service(App::new().configure(routes)).await;
|
|
527
|
+
let req = test::TestRequest::${endpoint.method.toLowerCase()}().uri("${endpoint.path}").to_request();
|
|
528
|
+
|
|
529
|
+
// Act
|
|
530
|
+
let resp = test::call_service(&app, req).await;
|
|
531
|
+
|
|
532
|
+
// Assert
|
|
533
|
+
assert!(resp.status().is_success());
|
|
534
|
+
}`;
|
|
535
|
+
}
|
|
536
|
+
else if (testFramework === 'junit') {
|
|
537
|
+
endpointTest = `
|
|
538
|
+
|
|
539
|
+
@Test
|
|
540
|
+
void should${endpoint.method}${endpoint.handler}Correctly() throws Exception {
|
|
541
|
+
// Act & Assert
|
|
542
|
+
mockMvc.perform(${endpoint.method.toLowerCase()}("${endpoint.path}"))
|
|
543
|
+
.andExpect(status().isOk());
|
|
544
|
+
}`;
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
endpointTest = `
|
|
548
|
+
|
|
549
|
+
it('should ${endpoint.method.toLowerCase()} ${endpoint.path} correctly', async () => {
|
|
550
|
+
// Arrange
|
|
551
|
+
const endpoint = '${endpoint.path}';
|
|
552
|
+
|
|
553
|
+
// Act
|
|
554
|
+
const response = await request(app).${endpoint.method.toLowerCase()}(endpoint);
|
|
555
|
+
|
|
556
|
+
// Assert
|
|
557
|
+
expect(response.status).toBe(200);
|
|
558
|
+
});`;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// @ts-ignore - Audit cleanup unused variable
|
|
562
|
+
const structureLabel = testFramework === 'pytest' ? 'def test_' : testFramework === 'flutter_test' ? 'test(' : 'describe(';
|
|
563
|
+
// @ts-ignore - Audit cleanup unused variable
|
|
564
|
+
const openingBracket = testFramework === 'pytest' ? ':' : testFramework === 'flutter_test' ? ', () async {' : ", () => {";
|
|
565
|
+
return `# 🔬 Template: TDD — Test-Driven Development
|
|
566
|
+
|
|
567
|
+
> RED → GREEN → REFACTOR. Nesta ordem. Sempre.
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Estrutura de Testes (${testFramework})
|
|
572
|
+
|
|
573
|
+
\`\`\`${testFramework === 'pytest' ? 'python' : testFramework === 'flutter_test' ? 'dart' : testFramework === 'go_test' ? 'go' : testFramework === 'cargo_test' ? 'rust' : testFramework === 'junit' ? 'java' : 'typescript'}
|
|
574
|
+
${testFramework === 'pytest'
|
|
575
|
+
? `import pytest
|
|
576
|
+
from app.services.${moduleName.toLowerCase()} import ${moduleName}Service
|
|
577
|
+
|
|
578
|
+
class Test${moduleName}:
|
|
579
|
+
"""Testes para ${moduleName}"""
|
|
580
|
+
|
|
581
|
+
${exampleTest}
|
|
582
|
+
|
|
583
|
+
def test_should_throw_error_when_invalid_input(self):
|
|
584
|
+
# Arrange
|
|
585
|
+
invalid_input = None
|
|
586
|
+
|
|
587
|
+
# Act & Assert
|
|
588
|
+
with pytest.raises(ValueError):
|
|
589
|
+
service.${methodName}(invalid_input)`
|
|
590
|
+
: testFramework === 'flutter_test'
|
|
591
|
+
? `import 'package:flutter_test/flutter_test.dart';
|
|
592
|
+
import 'package:${moduleName.toLowerCase()}/services/${moduleName.toLowerCase()}_service.dart';
|
|
593
|
+
|
|
594
|
+
void main() {
|
|
595
|
+
group('${moduleName}Service', () {
|
|
596
|
+
|
|
597
|
+
${exampleTest}
|
|
598
|
+
|
|
599
|
+
test('should throw exception when input is invalid', () {
|
|
600
|
+
// Arrange
|
|
601
|
+
final invalid = null;
|
|
602
|
+
|
|
603
|
+
// Act & Assert
|
|
604
|
+
expect(() => service.${methodName}(invalid), throwsException);
|
|
605
|
+
});${endpointTest}
|
|
606
|
+
});
|
|
607
|
+
}`
|
|
608
|
+
: testFramework === 'go_test'
|
|
609
|
+
? `package ${moduleName.toLowerCase()}_test
|
|
610
|
+
|
|
611
|
+
import (
|
|
612
|
+
"testing"
|
|
613
|
+
"net/http"
|
|
614
|
+
"net/http/httptest"
|
|
615
|
+
"github.com/stretchr/testify/assert"
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
// ── Happy Path ──
|
|
619
|
+
${exampleTest}
|
|
620
|
+
|
|
621
|
+
// ── Error Path ──
|
|
622
|
+
func TestShould_Return_Error_When_InvalidInput(t *testing.T) {
|
|
623
|
+
// Arrange
|
|
624
|
+
input := ${moduleName}Input{}
|
|
625
|
+
|
|
626
|
+
// Act
|
|
627
|
+
_, err := service.${methodName}(input)
|
|
628
|
+
|
|
629
|
+
// Assert
|
|
630
|
+
assert.Error(t, err)
|
|
631
|
+
}${endpointTest}`
|
|
632
|
+
: testFramework === 'cargo_test'
|
|
633
|
+
? `#[cfg(test)]
|
|
634
|
+
mod tests {
|
|
635
|
+
use super::*;
|
|
636
|
+
|
|
637
|
+
// ── Happy Path ──
|
|
638
|
+
${exampleTest}
|
|
639
|
+
|
|
640
|
+
// ── Error Path ──
|
|
641
|
+
#[test]
|
|
642
|
+
fn test_should_return_error_when_invalid_input() {
|
|
643
|
+
// Arrange
|
|
644
|
+
let input = ${moduleName}Input { name: "".to_string() };
|
|
645
|
+
|
|
646
|
+
// Act
|
|
647
|
+
let result = service.${methodName}(&input);
|
|
648
|
+
|
|
649
|
+
// Assert
|
|
650
|
+
assert!(result.is_err());
|
|
651
|
+
}${endpointTest}
|
|
652
|
+
}`
|
|
653
|
+
: testFramework === 'junit'
|
|
654
|
+
? `import org.junit.jupiter.api.Test;
|
|
655
|
+
import static org.junit.jupiter.api.Assertions.*;
|
|
656
|
+
|
|
657
|
+
class ${moduleName}Test {
|
|
658
|
+
|
|
659
|
+
// ── Happy Path ──
|
|
660
|
+
${exampleTest}
|
|
661
|
+
|
|
662
|
+
// ── Error Path ──
|
|
663
|
+
@Test
|
|
664
|
+
void shouldThrowWhenInvalidInput() {
|
|
665
|
+
// Arrange
|
|
666
|
+
var invalidInput = new ${moduleName}Input(null);
|
|
667
|
+
|
|
668
|
+
// Act & Assert
|
|
669
|
+
assertThrows(IllegalArgumentException.class,
|
|
670
|
+
() -> service.${methodName}(invalidInput));
|
|
671
|
+
}${endpointTest}
|
|
672
|
+
}`
|
|
673
|
+
: `describe('${moduleName}', () => {
|
|
674
|
+
|
|
675
|
+
describe('${methodName}', () => {
|
|
676
|
+
|
|
677
|
+
// ── Happy Path ──
|
|
678
|
+
${exampleTest}
|
|
679
|
+
|
|
680
|
+
// ── Error Path ──
|
|
681
|
+
it('should throw [erro] when [condição inválida]', () => {
|
|
682
|
+
// Arrange
|
|
683
|
+
const invalidInput = undefined;
|
|
684
|
+
|
|
685
|
+
// Act & Assert
|
|
686
|
+
expect(() => service.${methodName}(invalidInput)).toThrow(Error);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// ── Boundary ──
|
|
690
|
+
it('should handle empty input', () => {
|
|
691
|
+
// Arrange
|
|
692
|
+
const boundaryInput = {};
|
|
693
|
+
|
|
694
|
+
// Act
|
|
695
|
+
const result = service.${methodName}(boundaryInput);
|
|
696
|
+
|
|
697
|
+
// Assert
|
|
698
|
+
expect(result).toBeDefined();
|
|
699
|
+
});${endpointTest}
|
|
700
|
+
});
|
|
701
|
+
});`}
|
|
702
|
+
\`\`\`
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## Ciclo TDD
|
|
707
|
+
|
|
708
|
+
\`\`\`
|
|
709
|
+
1. RED: Escrever teste que FALHA
|
|
710
|
+
2. GREEN: Escrever código MÍNIMO para passar
|
|
711
|
+
3. REFACTOR: Melhorar sem quebrar testes
|
|
712
|
+
4. REPEAT
|
|
713
|
+
\`\`\`
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## Checklist
|
|
718
|
+
|
|
719
|
+
\`\`\`
|
|
720
|
+
□ Teste escrito ANTES do código
|
|
721
|
+
□ Teste falha antes da implementação (RED)
|
|
722
|
+
□ Implementação mínima para passar (GREEN)
|
|
723
|
+
□ Refatoração sem quebrar testes (REFACTOR)
|
|
724
|
+
□ Happy path coberto
|
|
725
|
+
□ Error path coberto
|
|
726
|
+
□ Boundary cases cobertos
|
|
727
|
+
□ Cobertura atinge o mínimo do projeto
|
|
728
|
+
\`\`\`
|
|
729
|
+
`;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Generate ADR template with optional pre-filled examples from tech stack
|
|
733
|
+
*/
|
|
734
|
+
export function generateAdrTemplate(ctx) {
|
|
735
|
+
const enriched = getEnriched(ctx);
|
|
736
|
+
const stack = ctx && 'stack' in ctx ? ctx.stack : undefined;
|
|
737
|
+
const domain = enriched.domain;
|
|
738
|
+
let exampleDecision = '[Título da Decisão]';
|
|
739
|
+
let contextDescription = '[descrever o contexto de negócio e técnico]';
|
|
740
|
+
let decisionDescription = '[descrever a decisão claramente]';
|
|
741
|
+
let alternativeOne = '[alternativa 1]';
|
|
742
|
+
let alternativeTwo = '[alternativa 2]';
|
|
743
|
+
// Generate tech stack specific examples (framework-aware)
|
|
744
|
+
if (stack) {
|
|
745
|
+
const framework = stack.frameworks[0] || 'Backend Framework';
|
|
746
|
+
const database = stack.frameworks.find((f) => ['PostgreSQL', 'MongoDB', 'MySQL', 'SQLite', 'Redis'].includes(f)) || 'PostgreSQL';
|
|
747
|
+
const adrLangs = stack.languages.map((l) => l.toLowerCase());
|
|
748
|
+
const adrIsPython = adrLangs.includes('python');
|
|
749
|
+
const adrIsDart = adrLangs.includes('dart');
|
|
750
|
+
const adrIsGo = adrLangs.includes('go');
|
|
751
|
+
const adrIsJava = adrLangs.includes('java') || adrLangs.includes('kotlin');
|
|
752
|
+
exampleDecision = `Uso de ${framework} para Backend`;
|
|
753
|
+
if (adrIsPython) {
|
|
754
|
+
contextDescription = `O projeto requer uma API REST escalável com Python. Precisamos escolher um framework que:
|
|
755
|
+
- Suporte async/await nativo
|
|
756
|
+
- Tenha tipagem com Pydantic e validação automática
|
|
757
|
+
- Tenha comunidade ativa e documentação excelente`;
|
|
758
|
+
decisionDescription = `Decidimos usar ${framework} como framework backend principal. ${framework} oferece:
|
|
759
|
+
- Async/await nativo com alto desempenho
|
|
760
|
+
- Validação automática via Pydantic
|
|
761
|
+
- Documentação OpenAPI/Swagger automática
|
|
762
|
+
- Suporte a bancos de dados (SQLAlchemy, Tortoise ORM, etc.)`;
|
|
763
|
+
alternativeOne = 'Django REST Framework';
|
|
764
|
+
alternativeTwo = 'Flask + extensões';
|
|
765
|
+
}
|
|
766
|
+
else if (adrIsDart) {
|
|
767
|
+
contextDescription = `O projeto requer um aplicativo mobile multiplataforma. Precisamos escolher um framework que:
|
|
768
|
+
- Suporte iOS e Android com um único codebase
|
|
769
|
+
- Tenha hot-reload e boa experiência de desenvolvimento
|
|
770
|
+
- Tenha widgets nativos e alta performance`;
|
|
771
|
+
decisionDescription = `Decidimos usar ${framework} como framework principal. ${framework} oferece:
|
|
772
|
+
- UI declarativa com widgets nativos
|
|
773
|
+
- Hot-reload para desenvolvimento rápido
|
|
774
|
+
- Compilação nativa para iOS e Android
|
|
775
|
+
- Ecossistema de pacotes via pub.dev`;
|
|
776
|
+
alternativeOne = 'React Native';
|
|
777
|
+
alternativeTwo = 'Kotlin Multiplatform';
|
|
778
|
+
}
|
|
779
|
+
else if (adrIsGo) {
|
|
780
|
+
contextDescription = `O projeto requer um serviço backend de alta performance. Precisamos escolher uma stack que:
|
|
781
|
+
- Tenha concorrência nativa eficiente
|
|
782
|
+
- Suporte compilação estática e deploy simplificado
|
|
783
|
+
- Tenha forte tipagem e performance previsível`;
|
|
784
|
+
decisionDescription = `Decidimos usar ${framework} como framework principal. ${framework} oferece:
|
|
785
|
+
- Goroutines para concorrência eficiente
|
|
786
|
+
- Binário estático único para deploy
|
|
787
|
+
- Performance previsível e baixo footprint
|
|
788
|
+
- Ecossistema robusto de middleware`;
|
|
789
|
+
alternativeOne = 'net/http padrão + chi router';
|
|
790
|
+
alternativeTwo = 'Fiber (Express-like para Go)';
|
|
791
|
+
}
|
|
792
|
+
else if (adrIsJava) {
|
|
793
|
+
contextDescription = `O projeto requer uma API REST enterprise-grade. Precisamos escolher um framework que:
|
|
794
|
+
- Suporte injeção de dependência nativa
|
|
795
|
+
- Tenha ecossistema maduro e produção comprovada
|
|
796
|
+
- Tenha integração com bancos de dados (JPA/Hibernate)`;
|
|
797
|
+
decisionDescription = `Decidimos usar ${framework} como framework backend principal. ${framework} oferece:
|
|
798
|
+
- Injeção de dependência nativa
|
|
799
|
+
- Ecossistema maduro com milhares de extensões
|
|
800
|
+
- Suporte a JPA/Hibernate para persistência
|
|
801
|
+
- Segurança via Spring Security / equivalente`;
|
|
802
|
+
alternativeOne = 'Quarkus (nativo GraalVM)';
|
|
803
|
+
alternativeTwo = 'Micronaut';
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
contextDescription = `O projeto requer uma API REST escalável com TypeScript. Precisamos escolher um framework que:
|
|
807
|
+
- Suporte TypeScript nativo
|
|
808
|
+
- Tenha decorators e dependency injection
|
|
809
|
+
- Tenha comunidade ativa e documentação excelente`;
|
|
810
|
+
decisionDescription = `Decidimos usar ${framework} como framework backend principal. ${framework} oferece:
|
|
811
|
+
- Arquitetura modular built-in
|
|
812
|
+
- Decorators para roteamento e middleware
|
|
813
|
+
- Injeção de dependência nativa
|
|
814
|
+
- Suporte a bancos de dados (TypeORM, Prisma, etc.)`;
|
|
815
|
+
alternativeOne = 'Express.js + middleware customizado';
|
|
816
|
+
alternativeTwo = 'Fastify';
|
|
817
|
+
}
|
|
818
|
+
// Add database-specific ADR if applicable
|
|
819
|
+
let dbDecision = '';
|
|
820
|
+
if (database) {
|
|
821
|
+
dbDecision = `
|
|
822
|
+
|
|
823
|
+
---
|
|
824
|
+
|
|
825
|
+
## ADR-002: Banco de Dados ${database}
|
|
826
|
+
|
|
827
|
+
**Status:** accepted
|
|
828
|
+
**Data:** 2024-01-15
|
|
829
|
+
**Autores:** [equipe técnica]
|
|
830
|
+
|
|
831
|
+
### Contexto
|
|
832
|
+
|
|
833
|
+
Precisamos escolher um banco de dados relacional que:
|
|
834
|
+
- Suporte transações ACID
|
|
835
|
+
- Tenha integração com ${framework}
|
|
836
|
+
- Permita escalabilidade horizontal
|
|
837
|
+
|
|
838
|
+
### Decisão
|
|
839
|
+
|
|
840
|
+
Escolhemos ${database} como banco de dados principal por:
|
|
841
|
+
- Suporte a JSON nativo
|
|
842
|
+
- Excelente performance em leitura/escrita
|
|
843
|
+
- Integração com ${adrIsPython ? 'SQLAlchemy/Alembic' : adrIsJava ? 'JPA/Hibernate' : adrIsGo ? 'GORM/sqlx' : 'TypeORM/Prisma'} é padrão
|
|
844
|
+
|
|
845
|
+
### Alternativas Consideradas
|
|
846
|
+
|
|
847
|
+
| # | Alternativa | Prós | Contras |
|
|
848
|
+
|---|-----------|------|---------|
|
|
849
|
+
| 1 | MySQL | Popular, estável | Menos recursos avançados |
|
|
850
|
+
| 2 | MongoDB | Escalável | Sem ACID nativo, schema dinâmico |`;
|
|
851
|
+
return `# 📋 Template: ADR — Architecture Decision Record
|
|
852
|
+
|
|
853
|
+
> Use quando uma decisão técnica é significativa ou controversa.
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
## ADR-001: ${exampleDecision}
|
|
858
|
+
|
|
859
|
+
**Status:** proposed | accepted | deprecated | superseded by ADR-YYY
|
|
860
|
+
**Data:** YYYY-MM-DD
|
|
861
|
+
**Autores:** [quem participou da decisão]
|
|
862
|
+
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
### Contexto
|
|
866
|
+
|
|
867
|
+
> Qual é o problema ou necessidade que levou a esta decisão?
|
|
868
|
+
|
|
869
|
+
${contextDescription}
|
|
870
|
+
|
|
871
|
+
---
|
|
872
|
+
|
|
873
|
+
### Decisão
|
|
874
|
+
|
|
875
|
+
> O que foi decidido?
|
|
876
|
+
|
|
877
|
+
${decisionDescription}
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
### Alternativas Consideradas
|
|
882
|
+
|
|
883
|
+
| # | Alternativa | Prós | Contras | Por que descartada |
|
|
884
|
+
|---|-----------|------|---------|-------------------|
|
|
885
|
+
| 1 | ${alternativeOne} | [prós] | [contras] | [motivo] |
|
|
886
|
+
| 2 | ${alternativeTwo} | [prós] | [contras] | [motivo] |
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
### Consequências
|
|
891
|
+
|
|
892
|
+
**Positivas:**
|
|
893
|
+
- Arquitetura clara e escalável
|
|
894
|
+
- Código mais organizado e testável
|
|
895
|
+
- Comunidade ativa para suporte
|
|
896
|
+
|
|
897
|
+
**Negativas:**
|
|
898
|
+
- Curva de aprendizado para novos desenvolvedores
|
|
899
|
+
- Overhead de dependências
|
|
900
|
+
|
|
901
|
+
**Riscos:**
|
|
902
|
+
- Atualização de versões major — probabilidade: média
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
### Notas
|
|
907
|
+
|
|
908
|
+
- Revisar em 6 meses se a decisão continua válida
|
|
909
|
+
${dbDecision}`;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
// Add domain-specific ADR section
|
|
913
|
+
let domainAdrs = '';
|
|
914
|
+
if (domain?.domain === 'fintech') {
|
|
915
|
+
domainAdrs = `
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
## ADR-XXX: Criptografia de CPF e Dados Sensíveis
|
|
920
|
+
|
|
921
|
+
**Status:** proposed
|
|
922
|
+
**Data:** YYYY-MM-DD
|
|
923
|
+
**Autores:** [equipe de segurança]
|
|
924
|
+
|
|
925
|
+
### Contexto
|
|
926
|
+
|
|
927
|
+
Dados de clientes (CPF, CNPJ, dados bancários) são críticos para fintech.
|
|
928
|
+
Precisamos de criptografia de dados sensíveis at-rest.
|
|
929
|
+
|
|
930
|
+
### Decisão
|
|
931
|
+
|
|
932
|
+
Usar AES-256 para criptografia de dados sensíveis com chaves armazenadas no vault.
|
|
933
|
+
|
|
934
|
+
### Alternativas Consideradas
|
|
935
|
+
|
|
936
|
+
| # | Alternativa | Segurança | Complexidade |
|
|
937
|
+
|---|-----------|----------|--------------|
|
|
938
|
+
| 1 | AES-256 | Alta | Média |
|
|
939
|
+
| 2 | RSA | Muito Alta | Alta |
|
|
940
|
+
| 3 | Nenhuma | Baixa | Baixa |`;
|
|
941
|
+
}
|
|
942
|
+
else if (domain?.domain === 'healthtech') {
|
|
943
|
+
domainAdrs = `
|
|
944
|
+
|
|
945
|
+
---
|
|
946
|
+
|
|
947
|
+
## ADR-XXX: Conformidade HIPAA em Armazenamento
|
|
948
|
+
|
|
949
|
+
**Status:** proposed
|
|
950
|
+
**Data:** YYYY-MM-DD
|
|
951
|
+
**Autores:** [equipe de compliance]
|
|
952
|
+
|
|
953
|
+
### Contexto
|
|
954
|
+
|
|
955
|
+
Dados de saúde devem estar em conformidade com HIPAA.
|
|
956
|
+
Precisamos garantir criptografia, auditoria e retenção apropriada.
|
|
957
|
+
|
|
958
|
+
### Decisão
|
|
959
|
+
|
|
960
|
+
Implementar criptografia AES-256, logging de acesso e retenção de dados por 7 anos.`;
|
|
961
|
+
}
|
|
962
|
+
return `# 📋 Template: ADR — Architecture Decision Record
|
|
963
|
+
|
|
964
|
+
> Use quando uma decisão técnica é significativa ou controversa.
|
|
965
|
+
|
|
966
|
+
---
|
|
967
|
+
|
|
968
|
+
## ADR-XXX: ${exampleDecision}
|
|
969
|
+
|
|
970
|
+
**Status:** proposed | accepted | deprecated | superseded by ADR-YYY
|
|
971
|
+
**Data:** YYYY-MM-DD
|
|
972
|
+
**Autores:** [quem participou da decisão]
|
|
973
|
+
|
|
974
|
+
---
|
|
975
|
+
|
|
976
|
+
### Contexto
|
|
977
|
+
|
|
978
|
+
> Qual é o problema ou necessidade que levou a esta decisão?
|
|
979
|
+
|
|
980
|
+
${contextDescription}
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
### Decisão
|
|
985
|
+
|
|
986
|
+
> O que foi decidido?
|
|
987
|
+
|
|
988
|
+
${decisionDescription}
|
|
989
|
+
|
|
990
|
+
---
|
|
991
|
+
|
|
992
|
+
### Alternativas Consideradas
|
|
993
|
+
|
|
994
|
+
| # | Alternativa | Prós | Contras | Por que descartada |
|
|
995
|
+
|---|-----------|------|---------|-------------------|
|
|
996
|
+
| 1 | ${alternativeOne} | [prós] | [contras] | [motivo] |
|
|
997
|
+
| 2 | ${alternativeTwo} | [prós] | [contras] | [motivo] |
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
1001
|
+
### Consequências
|
|
1002
|
+
|
|
1003
|
+
**Positivas:**
|
|
1004
|
+
- [consequência positiva 1]
|
|
1005
|
+
- [consequência positiva 2]
|
|
1006
|
+
|
|
1007
|
+
**Negativas:**
|
|
1008
|
+
- [consequência negativa 1]
|
|
1009
|
+
- [mitigação: como minimizar]
|
|
1010
|
+
|
|
1011
|
+
**Riscos:**
|
|
1012
|
+
- [risco 1] — probabilidade: [alta/média/baixa]
|
|
1013
|
+
|
|
1014
|
+
---
|
|
1015
|
+
|
|
1016
|
+
### Notas
|
|
1017
|
+
|
|
1018
|
+
- [qualquer informação adicional]${domainAdrs}
|
|
1019
|
+
`;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Generate Threat Model template with optional pre-filled domain-specific threats
|
|
1023
|
+
*/
|
|
1024
|
+
export function generateThreatModelTemplate(ctx) {
|
|
1025
|
+
const enriched = getEnriched(ctx);
|
|
1026
|
+
const domain = enriched.domain;
|
|
1027
|
+
// @ts-ignore - Audit cleanup unused variable
|
|
1028
|
+
const businessEntities = domain?.businessEntities || [];
|
|
1029
|
+
const compliance = domain?.compliance || [];
|
|
1030
|
+
let featureName = '[Nome]';
|
|
1031
|
+
let actors = `| [ator 1] | [alto/médio/baixo] | [dados/recursos] |`;
|
|
1032
|
+
let sensitiveData = `| [dado 1] | PII / Financeiro / Auth | [como proteger] |`;
|
|
1033
|
+
let domainSpecificThreats = '';
|
|
1034
|
+
// Generate domain-specific actors and threats
|
|
1035
|
+
if (domain?.domain === 'fintech') {
|
|
1036
|
+
featureName = 'Transferência Bancária';
|
|
1037
|
+
actors = `| Usuário | Alto | Conta bancária, saldo |
|
|
1038
|
+
| Sistema Interno | Alto | Todas as transações |
|
|
1039
|
+
| Banco Parceiro | Médio | Dados de rota |
|
|
1040
|
+
| Auditor (interno) | Médio | Logs de transações |`;
|
|
1041
|
+
sensitiveData = `| CPF | PII | AES-256 at-rest, TLS in-transit |
|
|
1042
|
+
| Número de Conta | Financeiro | AES-256 + masking |
|
|
1043
|
+
| Saldo | Financeiro | Hash verificável |
|
|
1044
|
+
| Histórico de Transações | Auditoria | Criptografado, signed |`;
|
|
1045
|
+
domainSpecificThreats = `
|
|
1046
|
+
|
|
1047
|
+
---
|
|
1048
|
+
|
|
1049
|
+
## Ameaças Específicas de Fintech
|
|
1050
|
+
|
|
1051
|
+
| Ameaça | Descrição | Mitigação |
|
|
1052
|
+
|--------|-----------|-----------|
|
|
1053
|
+
| Falsificação de Identidade | Hacker usa CPF falso para abrir conta | Validação com gov API, SMS 2FA |
|
|
1054
|
+
| Tamperização de Saldo | Atacante modifica saldo direto no DB | Checksums, transações com assinatura |
|
|
1055
|
+
| Roubo de Sessão | Hijack de cookie de autenticação | HTTPS, SameSite cookie, token rotation |
|
|
1056
|
+
| Negação de Transferência | Usuário nega ter feito transação | Auditoria, e-mail de confirmação |
|
|
1057
|
+
| Bloqueio de Serviço | DDoS na API de transações | Rate limiting, WAF, CDN |
|
|
1058
|
+
| Escalonamento de Permissão | Admin faz transferência não autorizada | RBAC, audit logging, segregação |`;
|
|
1059
|
+
}
|
|
1060
|
+
else if (domain?.domain === 'tax' || domain?.subDomain === 'tax-processing') {
|
|
1061
|
+
featureName = 'Cálculo e Envio de Imposto';
|
|
1062
|
+
actors = `| Contribuinte | Alto | Declaração, CPF, rendimentos |
|
|
1063
|
+
| Contador | Médio | Dados do cliente, acesso a declaração |
|
|
1064
|
+
| Receita Federal | Médio | Declaração enviada, comprovantes |
|
|
1065
|
+
| Auditor Interno | Médio | Logs de processamento, erros |`;
|
|
1066
|
+
sensitiveData = `| CPF/CNPJ | PII | AES-256, masking em logs |
|
|
1067
|
+
| Rendimentos Totais | Financeiro | Criptografado |
|
|
1068
|
+
| Deduções e Benefícios | Pessoal | Criptografado |
|
|
1069
|
+
| Histórico de Envios | Auditoria | Signed, imutável |`;
|
|
1070
|
+
domainSpecificThreats = `
|
|
1071
|
+
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
## Ameaças Específicas de Impostos
|
|
1075
|
+
|
|
1076
|
+
| Ameaça | Descrição | Mitigação |
|
|
1077
|
+
|--------|-----------|-----------|
|
|
1078
|
+
| Falsificação de Receita | Declarar rendimento falso | Cruzamento com dados da Receita Federal |
|
|
1079
|
+
| Tamperização de Cálculo | Hacker reduz imposto devido | Validação automática, recálculo periódico |
|
|
1080
|
+
| Vazamento de Dados | Exposição de CPF/rendimento | Criptografia, LGPD compliance |
|
|
1081
|
+
| Negação de Envio | Usuário nega envio ao fisco | Timestamp assinado, audit log |
|
|
1082
|
+
| Indisponibilidade na Época | Servidor cai no último dia | SLA 99.9%, failover automático |
|
|
1083
|
+
| Acesso não Autorizado | Contador acessa dado de cliente errado | RBAC granular, segregação de dados |`;
|
|
1084
|
+
}
|
|
1085
|
+
else if (domain?.domain === 'healthtech') {
|
|
1086
|
+
featureName = 'Acesso a Histórico Médico';
|
|
1087
|
+
actors = `| Paciente | Alto | Histórico médico, exames, receitas |
|
|
1088
|
+
| Médico | Alto | Prontuário do paciente |
|
|
1089
|
+
| Farmacêutico | Médio | Prescrições, alergias |
|
|
1090
|
+
| Auditor Compliance | Médio | Logs de acesso, consentimento |`;
|
|
1091
|
+
sensitiveData = `| Nome Paciente | PII | Criptografado, HIPAA compliant |
|
|
1092
|
+
| Histórico Médico | Saúde | AES-256, acesso restrito |
|
|
1093
|
+
| Medicamentos | Saúde | Criptografado, masking em logs |
|
|
1094
|
+
| Resultado de Exames | Saúde | Criptografado, assinado digitalmente |`;
|
|
1095
|
+
domainSpecificThreats = `
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
|
|
1099
|
+
## Ameaças Específicas de Healthtech
|
|
1100
|
+
|
|
1101
|
+
| Ameaça | Descrição | Mitigação |
|
|
1102
|
+
|--------|-----------|-----------|
|
|
1103
|
+
| Acesso não Autorizado | Outro médico lê prontuário | RBAC por especialidade, audit |
|
|
1104
|
+
| Adulteração de Histórico | Alterar resultado de exame | Blockchain/assinatura digital |
|
|
1105
|
+
| Vazamento de Dados | Exposição de histórico médico | Criptografia, isolamento de rede |
|
|
1106
|
+
| Negação de Diagnóstico | Paciente nega consentimento | Digital signature + timestamp |
|
|
1107
|
+
| Indisponibilidade | Sistema cai durante consulta | Backup em tempo real, SLA 99.95% |
|
|
1108
|
+
| Privilégio Elevado | Admin lê dados de qualquer paciente | Segregação de funções, logging |`;
|
|
1109
|
+
}
|
|
1110
|
+
return `# 🛡️ Template: Threat Model (STRIDE)
|
|
1111
|
+
|
|
1112
|
+
> Use para features que lidam com dados sensíveis, pagamentos, autenticação.
|
|
1113
|
+
|
|
1114
|
+
---
|
|
1115
|
+
|
|
1116
|
+
## Feature: ${featureName}
|
|
1117
|
+
|
|
1118
|
+
### Atores e Assets
|
|
1119
|
+
|
|
1120
|
+
| Ator | Nível de Confiança | Assets que Acessa |
|
|
1121
|
+
|------|-------------------|------------------|
|
|
1122
|
+
${actors}
|
|
1123
|
+
|
|
1124
|
+
---
|
|
1125
|
+
|
|
1126
|
+
### Análise STRIDE
|
|
1127
|
+
|
|
1128
|
+
| Categoria | Ameaça | Probabilidade | Impacto | Mitigação |
|
|
1129
|
+
|-----------|--------|-------------|---------|-----------|
|
|
1130
|
+
| **S**poofing | Identidade falsa / autenticação fraca | M | A | MFA, JWT assinado, validação gov |
|
|
1131
|
+
| **T**ampering | Alteração de dados em trânsito/repouso | M | A | HTTPS, assinatura digital, checksums |
|
|
1132
|
+
| **R**epudiation | Negar ação realizada | M | A | Audit log detalhado com timestamp |
|
|
1133
|
+
| **I**nformation Disclosure | Vazamento de dados sensíveis | A | A | Criptografia AES-256, LGPD compliance |
|
|
1134
|
+
| **D**enial of Service | Serviço indisponível (DDoS, travamento) | M | A | Rate limiting, WAF, scaling automático |
|
|
1135
|
+
| **E**levation of Privilege | Escalar permissão (user → admin) | M | A | RBAC granular, segregação de funções |
|
|
1136
|
+
|
|
1137
|
+
---
|
|
1138
|
+
|
|
1139
|
+
### Dados Sensíveis
|
|
1140
|
+
|
|
1141
|
+
| Dado | Classificação | Proteção |
|
|
1142
|
+
|------|-------------|----------|
|
|
1143
|
+
${sensitiveData}
|
|
1144
|
+
|
|
1145
|
+
---
|
|
1146
|
+
|
|
1147
|
+
### Conformidade e Requisitos Regulatórios
|
|
1148
|
+
|
|
1149
|
+
${compliance.length > 0
|
|
1150
|
+
? `| Regulamento | Aplicável | Verificação |
|
|
1151
|
+
|-------------|-----------|-------------|
|
|
1152
|
+
${compliance.map((c) => `| ${c.name} | Sim | ${c.mandatoryChecks[0] || 'Auditoria'} |`).join('\n')}`
|
|
1153
|
+
: `| Regulamento | Aplicável | Verificação |
|
|
1154
|
+
|-------------|-----------|-------------|
|
|
1155
|
+
| LGPD (dados pessoais) | Depende | Consentimento, direito ao esquecimento |
|
|
1156
|
+
| Conformidade de Dados | Depende | Criptografia, HTTPS, audit logs |`}
|
|
1157
|
+
|
|
1158
|
+
---
|
|
1159
|
+
|
|
1160
|
+
### Checklist de Segurança
|
|
1161
|
+
|
|
1162
|
+
\`\`\`
|
|
1163
|
+
□ Input validado e sanitizado
|
|
1164
|
+
□ Output encodado (XSS prevention)
|
|
1165
|
+
□ Queries parametrizadas (SQL injection)
|
|
1166
|
+
□ Autenticação obrigatória (MFA quando sensível)
|
|
1167
|
+
□ Autorização por role/permission (RBAC)
|
|
1168
|
+
□ Dados sensíveis criptografados at rest (AES-256)
|
|
1169
|
+
□ Dados sensíveis criptografados in transit (TLS 1.2+)
|
|
1170
|
+
□ Rate limiting implementado
|
|
1171
|
+
□ Audit log para ações sensíveis (timestamp + usuário)
|
|
1172
|
+
□ Secrets em variáveis de ambiente (não hardcoded)
|
|
1173
|
+
□ Consentimento e transparência (LGPD/GDPR)
|
|
1174
|
+
□ Testes de penetração agendados
|
|
1175
|
+
\`\`\`
|
|
1176
|
+
${domainSpecificThreats}
|
|
1177
|
+
`;
|
|
1178
|
+
}
|
|
1179
|
+
//# sourceMappingURL=index.js.map
|