@creative-ia/cortex 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/config/cloud-proxy.d.ts +15 -0
- package/dist/config/cloud-proxy.js +63 -0
- package/dist/config/cloudwatch-store.d.ts +13 -0
- package/dist/config/cloudwatch-store.js +66 -0
- package/dist/config/license.d.ts +29 -0
- package/dist/config/license.js +165 -0
- package/dist/config/ssm-store.d.ts +2 -0
- package/dist/config/ssm-store.js +38 -0
- package/dist/config/telemetry.d.ts +17 -0
- package/dist/config/telemetry.js +93 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +460 -0
- package/dist/knowledge/dynamo-store.d.ts +17 -0
- package/dist/knowledge/dynamo-store.js +85 -0
- package/dist/knowledge/embeddings.d.ts +2 -0
- package/dist/knowledge/embeddings.js +36 -0
- package/dist/knowledge/loader.d.ts +8 -0
- package/dist/knowledge/loader.js +57 -0
- package/dist/lambda-package.zip +0 -0
- package/dist/lambda.d.ts +22 -0
- package/dist/lambda.js +496 -0
- package/dist/package.json +1 -0
- package/dist/tools/advance-process.d.ts +7 -0
- package/dist/tools/advance-process.js +128 -0
- package/dist/tools/analyze-code.d.ts +7 -0
- package/dist/tools/analyze-code.js +131 -0
- package/dist/tools/analyze-docs.d.ts +8 -0
- package/dist/tools/analyze-docs.js +147 -0
- package/dist/tools/config-registry.d.ts +3 -0
- package/dist/tools/config-registry.js +20 -0
- package/dist/tools/create-process.d.ts +6 -0
- package/dist/tools/create-process.js +257 -0
- package/dist/tools/decompose-epic.d.ts +7 -0
- package/dist/tools/decompose-epic.js +603 -0
- package/dist/tools/diagrams.d.ts +51 -0
- package/dist/tools/diagrams.js +304 -0
- package/dist/tools/generate-report.d.ts +9 -0
- package/dist/tools/generate-report.js +891 -0
- package/dist/tools/generate-wiki.d.ts +10 -0
- package/dist/tools/generate-wiki.js +700 -0
- package/dist/tools/get-architecture.d.ts +6 -0
- package/dist/tools/get-architecture.js +78 -0
- package/dist/tools/get-code-standards.d.ts +7 -0
- package/dist/tools/get-code-standards.js +52 -0
- package/dist/tools/init-process.d.ts +7 -0
- package/dist/tools/init-process.js +82 -0
- package/dist/tools/knowledge-crud.d.ts +26 -0
- package/dist/tools/knowledge-crud.js +142 -0
- package/dist/tools/logo-base64.d.ts +1 -0
- package/dist/tools/logo-base64.js +1 -0
- package/dist/tools/logs-query.d.ts +15 -0
- package/dist/tools/logs-query.js +46 -0
- package/dist/tools/reverse-engineer.d.ts +13 -0
- package/dist/tools/reverse-engineer.js +956 -0
- package/dist/tools/semantic-search.d.ts +7 -0
- package/dist/tools/semantic-search.js +68 -0
- package/dist/tools/update-process.d.ts +17 -0
- package/dist/tools/update-process.js +195 -0
- package/dist/tools/validate-idea.d.ts +7 -0
- package/dist/tools/validate-idea.js +339 -0
- package/dist/tools/validate-process.d.ts +6 -0
- package/dist/tools/validate-process.js +102 -0
- package/package.json +31 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool MCP: semantic_search — Busca semântica no knowledge base via Bedrock Embeddings.
|
|
3
|
+
*
|
|
4
|
+
* Fluxo:
|
|
5
|
+
* 1. Gera embedding da query do usuário
|
|
6
|
+
* 2. Carrega todos os registros do(s) domain(s)
|
|
7
|
+
* 3. Gera embedding de cada registro (título + conteúdo truncado)
|
|
8
|
+
* 4. Calcula cosine similarity
|
|
9
|
+
* 5. Retorna top-N mais relevantes
|
|
10
|
+
*
|
|
11
|
+
* Cache: embeddings são gerados on-the-fly (dataset pequeno, ~100 registros).
|
|
12
|
+
* Para datasets maiores, considerar cache em DynamoDB.
|
|
13
|
+
*/
|
|
14
|
+
import { getEmbedding, cosineSimilarity } from "../knowledge/embeddings.js";
|
|
15
|
+
import { queryByDomain } from "../knowledge/dynamo-store.js";
|
|
16
|
+
import { filterAllowedDomains } from "../config/license.js";
|
|
17
|
+
const ALL_DOMAINS = [
|
|
18
|
+
"decisions", "changelog", "troubleshooting", "glossary",
|
|
19
|
+
"integrations", "lessons-learned", "pending", "ports",
|
|
20
|
+
"stakeholders", "environments",
|
|
21
|
+
];
|
|
22
|
+
export async function semanticSearch(params) {
|
|
23
|
+
const limit = params.limit || 5;
|
|
24
|
+
const allDomains = params.domain ? [params.domain] : ALL_DOMAINS;
|
|
25
|
+
const domains = params.license ? filterAllowedDomains(params.license, allDomains) : allDomains;
|
|
26
|
+
// 1. Get query embedding
|
|
27
|
+
const queryEmb = await getEmbedding(params.query);
|
|
28
|
+
// 2. Load all entries from target domains
|
|
29
|
+
const allEntries = [];
|
|
30
|
+
for (const d of domains) {
|
|
31
|
+
const entries = await queryByDomain(d, 200);
|
|
32
|
+
allEntries.push(...entries);
|
|
33
|
+
}
|
|
34
|
+
if (allEntries.length === 0) {
|
|
35
|
+
return "Nenhum registro encontrado nos domains selecionados.";
|
|
36
|
+
}
|
|
37
|
+
// 3. Score each entry
|
|
38
|
+
const scored = [];
|
|
39
|
+
for (const entry of allEntries) {
|
|
40
|
+
const text = `${entry.title}. ${entry.content.slice(0, 500)}`;
|
|
41
|
+
try {
|
|
42
|
+
const entryEmb = await getEmbedding(text);
|
|
43
|
+
const score = cosineSimilarity(queryEmb, entryEmb);
|
|
44
|
+
scored.push({ entry, score });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Skip entries that fail embedding
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// 4. Sort by similarity and take top-N
|
|
51
|
+
scored.sort((a, b) => b.score - a.score);
|
|
52
|
+
const top = scored.slice(0, limit);
|
|
53
|
+
if (top.length === 0) {
|
|
54
|
+
return "Não foi possível calcular similaridade para os registros.";
|
|
55
|
+
}
|
|
56
|
+
// 5. Format results
|
|
57
|
+
const lines = [`## Semantic Search: "${params.query}" (top ${top.length})\n`];
|
|
58
|
+
for (const { entry, score } of top) {
|
|
59
|
+
const pct = (score * 100).toFixed(1);
|
|
60
|
+
lines.push(`### [${entry.domain}] ${entry.id} — ${entry.title} (${pct}% match)`);
|
|
61
|
+
const preview = entry.content.length > 300 ? entry.content.slice(0, 300) + "..." : entry.content;
|
|
62
|
+
lines.push(preview);
|
|
63
|
+
if (entry.tags?.length)
|
|
64
|
+
lines.push(`Tags: ${entry.tags.join(", ")}`);
|
|
65
|
+
lines.push("");
|
|
66
|
+
}
|
|
67
|
+
return lines.join("\n");
|
|
68
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface UpdateProcessRequest {
|
|
2
|
+
processId: string;
|
|
3
|
+
updates: {
|
|
4
|
+
tech_stack?: Record<string, string>;
|
|
5
|
+
decisions?: Array<{
|
|
6
|
+
title: string;
|
|
7
|
+
decision: string;
|
|
8
|
+
rationale?: string;
|
|
9
|
+
}>;
|
|
10
|
+
phase_status?: Record<string, string>;
|
|
11
|
+
notes?: string;
|
|
12
|
+
stakeholder?: string;
|
|
13
|
+
};
|
|
14
|
+
listProcesses?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function updateProcess(params: UpdateProcessRequest): Promise<string>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: update_process
|
|
3
|
+
* Acrescenta informações a um processo existente.
|
|
4
|
+
*
|
|
5
|
+
* Fluxo:
|
|
6
|
+
* 1. Busca processo no DynamoDB pelo processId
|
|
7
|
+
* 2. Valida que o processo existe
|
|
8
|
+
* 3. Aplica as atualizações (tech_stack, decisions, phase_status, notes)
|
|
9
|
+
* 4. Atualiza DynamoDB
|
|
10
|
+
* 5. Retorna conteúdo atualizado com marcadores CREATE_FILE para o MCP client
|
|
11
|
+
*/
|
|
12
|
+
import { getEntry, putEntry, queryByDomain } from "../knowledge/dynamo-store.js";
|
|
13
|
+
/**
|
|
14
|
+
* Lista processos acessíveis pela licença (domain: processes).
|
|
15
|
+
*/
|
|
16
|
+
async function listAccessibleProcesses() {
|
|
17
|
+
const entries = await queryByDomain("processes", 50);
|
|
18
|
+
if (entries.length === 0)
|
|
19
|
+
return "Nenhum processo encontrado.";
|
|
20
|
+
const lines = ["## Processos Disponíveis", ""];
|
|
21
|
+
lines.push("| UUID | Título | Status | Stakeholder |");
|
|
22
|
+
lines.push("|------|--------|--------|-------------|");
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
let content;
|
|
25
|
+
try {
|
|
26
|
+
content = JSON.parse(entry.content);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const uuid = content.uuid || entry.id.replace("PROC-", "");
|
|
32
|
+
const status = content.status || "unknown";
|
|
33
|
+
const stakeholder = content.stakeholder || "—";
|
|
34
|
+
lines.push(`| ${uuid} | ${entry.title} | ${status} | ${stakeholder} |`);
|
|
35
|
+
}
|
|
36
|
+
return lines.join("\n");
|
|
37
|
+
}
|
|
38
|
+
export async function updateProcess(params) {
|
|
39
|
+
const { processId, updates, listProcesses: shouldList } = params;
|
|
40
|
+
// Se pediu para listar, retorna lista
|
|
41
|
+
if (shouldList) {
|
|
42
|
+
return listAccessibleProcesses();
|
|
43
|
+
}
|
|
44
|
+
// Busca processo
|
|
45
|
+
const procId = processId.startsWith("PROC-") ? processId : `PROC-${processId}`;
|
|
46
|
+
const entry = await getEntry("processes", procId);
|
|
47
|
+
if (!entry) {
|
|
48
|
+
// Tenta listar processos disponíveis para ajudar
|
|
49
|
+
const list = await listAccessibleProcesses();
|
|
50
|
+
return `Processo "${processId}" não encontrado.\n\n${list}`;
|
|
51
|
+
}
|
|
52
|
+
// Parse conteúdo existente
|
|
53
|
+
let content;
|
|
54
|
+
try {
|
|
55
|
+
content = JSON.parse(entry.content);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return `Erro ao parsear conteúdo do processo ${processId}.`;
|
|
59
|
+
}
|
|
60
|
+
const out = [];
|
|
61
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
62
|
+
// --- Aplica tech_stack ---
|
|
63
|
+
if (updates.tech_stack) {
|
|
64
|
+
content.tech_stack = { ...(content.tech_stack || {}), ...updates.tech_stack };
|
|
65
|
+
out.push("✅ Tech stack atualizado");
|
|
66
|
+
}
|
|
67
|
+
// --- Aplica decisions ---
|
|
68
|
+
if (updates.decisions && updates.decisions.length > 0) {
|
|
69
|
+
if (!content.decisions)
|
|
70
|
+
content.decisions = [];
|
|
71
|
+
for (const dec of updates.decisions) {
|
|
72
|
+
const nextId = `DEC-${String(content.decisions.length + 1).padStart(3, "0")}`;
|
|
73
|
+
content.decisions.push({
|
|
74
|
+
id: nextId,
|
|
75
|
+
date,
|
|
76
|
+
title: dec.title,
|
|
77
|
+
decision: dec.decision,
|
|
78
|
+
rationale: dec.rationale,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
out.push(`✅ ${updates.decisions.length} decisão(ões) adicionada(s)`);
|
|
82
|
+
}
|
|
83
|
+
// --- Aplica phase_status ---
|
|
84
|
+
if (updates.phase_status) {
|
|
85
|
+
for (const [phase, status] of Object.entries(updates.phase_status)) {
|
|
86
|
+
if (content.phases[phase] !== undefined) {
|
|
87
|
+
content.phases[phase] = status;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
out.push("✅ Status de fases atualizado");
|
|
91
|
+
}
|
|
92
|
+
// --- Aplica notes ---
|
|
93
|
+
if (updates.notes) {
|
|
94
|
+
if (!content.notes)
|
|
95
|
+
content.notes = [];
|
|
96
|
+
content.notes.push(`[${date}] ${updates.notes}`);
|
|
97
|
+
out.push("✅ Nota adicionada");
|
|
98
|
+
}
|
|
99
|
+
// --- Aplica stakeholder ---
|
|
100
|
+
if (updates.stakeholder) {
|
|
101
|
+
content.stakeholder = updates.stakeholder;
|
|
102
|
+
out.push("✅ Stakeholder atualizado");
|
|
103
|
+
}
|
|
104
|
+
// --- Atualiza status geral ---
|
|
105
|
+
const phaseValues = Object.values(content.phases);
|
|
106
|
+
if (phaseValues.every(s => s === "done")) {
|
|
107
|
+
content.status = "completed";
|
|
108
|
+
}
|
|
109
|
+
else if (phaseValues.some(s => s === "in_progress" || s === "done")) {
|
|
110
|
+
content.status = "in_progress";
|
|
111
|
+
}
|
|
112
|
+
// --- Persiste no DynamoDB ---
|
|
113
|
+
const techSummary = content.tech_stack
|
|
114
|
+
? Object.values(content.tech_stack).join(" | ")
|
|
115
|
+
: "";
|
|
116
|
+
await putEntry({
|
|
117
|
+
domain: "processes",
|
|
118
|
+
id: procId,
|
|
119
|
+
title: entry.title,
|
|
120
|
+
content: JSON.stringify(content),
|
|
121
|
+
tags: entry.tags || ["process", "active"],
|
|
122
|
+
metadata: {
|
|
123
|
+
stakeholder: content.stakeholder,
|
|
124
|
+
createdBy: "mcp-server",
|
|
125
|
+
status: content.status,
|
|
126
|
+
...(techSummary && { tech_stack: techSummary }),
|
|
127
|
+
},
|
|
128
|
+
created_at: entry.created_at,
|
|
129
|
+
updated_at: new Date().toISOString(),
|
|
130
|
+
});
|
|
131
|
+
// --- Gera arquivos locais atualizados ---
|
|
132
|
+
const basePath = content.localPath || `orquestrador/creative-main/process/${content.uuid}`;
|
|
133
|
+
// knowledge.json atualizado
|
|
134
|
+
const knowledgeJson = JSON.stringify({
|
|
135
|
+
process_id: content.uuid,
|
|
136
|
+
version: "1.1.0",
|
|
137
|
+
title: `Knowledge Base — ${entry.title}`,
|
|
138
|
+
created_at: entry.created_at?.slice(0, 10) || date,
|
|
139
|
+
updated_at: date,
|
|
140
|
+
status: content.status,
|
|
141
|
+
meetings: [],
|
|
142
|
+
decisions: content.decisions || [],
|
|
143
|
+
questions: [],
|
|
144
|
+
context: content.tech_stack ? [{
|
|
145
|
+
id: "CTX-001",
|
|
146
|
+
date,
|
|
147
|
+
title: "Tech Stack",
|
|
148
|
+
content: techSummary,
|
|
149
|
+
}] : [],
|
|
150
|
+
learnings: [],
|
|
151
|
+
notes: content.notes || [],
|
|
152
|
+
}, null, 2);
|
|
153
|
+
// orchestration.json atualizado
|
|
154
|
+
const orchestrationJson = JSON.stringify({
|
|
155
|
+
process_id: content.uuid,
|
|
156
|
+
version: "1.1.0",
|
|
157
|
+
created_at: entry.created_at?.slice(0, 10) || date,
|
|
158
|
+
phases: Object.fromEntries(Object.entries(content.phases).map(([k, v]) => [
|
|
159
|
+
k.replace(/^\d+-/, ""),
|
|
160
|
+
{
|
|
161
|
+
status: v,
|
|
162
|
+
started_at: v !== "pending" ? date : null,
|
|
163
|
+
completed_at: v === "done" ? date : null,
|
|
164
|
+
},
|
|
165
|
+
])),
|
|
166
|
+
...(content.tech_stack && { tech_stack: content.tech_stack }),
|
|
167
|
+
repos: [],
|
|
168
|
+
sessions: [{ date, type: "update_process", summary: out.join(", ") }],
|
|
169
|
+
}, null, 2);
|
|
170
|
+
// Monta output com marcadores CREATE_FILE
|
|
171
|
+
const result = [
|
|
172
|
+
`## Processo ${content.uuid} — Atualizado`,
|
|
173
|
+
"",
|
|
174
|
+
...out,
|
|
175
|
+
"",
|
|
176
|
+
`DynamoDB: [processes] ${procId} ✅`,
|
|
177
|
+
"",
|
|
178
|
+
"---",
|
|
179
|
+
"",
|
|
180
|
+
"## 📁 Arquivos Locais (atualizar automaticamente)",
|
|
181
|
+
"",
|
|
182
|
+
`> Diretório base: \`${basePath}/\``,
|
|
183
|
+
"",
|
|
184
|
+
`<!-- CREATE_FILE: ${basePath}/knowledge.json -->`,
|
|
185
|
+
"```json",
|
|
186
|
+
knowledgeJson,
|
|
187
|
+
"```",
|
|
188
|
+
"",
|
|
189
|
+
`<!-- CREATE_FILE: ${basePath}/orchestration.json -->`,
|
|
190
|
+
"```json",
|
|
191
|
+
orchestrationJson,
|
|
192
|
+
"```",
|
|
193
|
+
];
|
|
194
|
+
return result.join("\n");
|
|
195
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: validate_idea
|
|
3
|
+
* Recebe uma ideia em linguagem natural e retorna análise de viabilidade
|
|
4
|
+
* consultando internamente: domínios, integrações, arquitetura, processos anteriores.
|
|
5
|
+
* O cliente NUNCA vê o knowledge bruto — recebe apenas o parecer processado.
|
|
6
|
+
*/
|
|
7
|
+
import { loadCodeStandards, loadIntegrations, loadDecisions, loadIndex } from "../knowledge/loader.js";
|
|
8
|
+
const DOMAINS = [
|
|
9
|
+
{ name: "Identity", type: "core", squad: "Squad Identity" },
|
|
10
|
+
{ name: "Onboarding", type: "core", squad: "Squad Aquisicao" },
|
|
11
|
+
{ name: "Cards", type: "core", squad: "Squad Cartoes" },
|
|
12
|
+
{ name: "Wallet", type: "core", squad: "Squad Cartoes" },
|
|
13
|
+
{ name: "Growth", type: "core", squad: "Squad Aquisicao" },
|
|
14
|
+
{ name: "Accounts", type: "supporting", squad: "Squad Contas" },
|
|
15
|
+
{ name: "Transactions", type: "supporting", squad: "Squad Financeiro" },
|
|
16
|
+
{ name: "Billing", type: "supporting", squad: "Squad Financeiro" },
|
|
17
|
+
{ name: "Collections", type: "supporting", squad: "Squad Pos-Venda" },
|
|
18
|
+
{ name: "Disputes", type: "supporting", squad: "Squad Pos-Venda" },
|
|
19
|
+
{ name: "Notifications", type: "supporting", squad: "Squad Platform" },
|
|
20
|
+
{ name: "Platform", type: "generic", squad: "Squad Platform" },
|
|
21
|
+
{ name: "Design System", type: "generic", squad: "Squad Design" },
|
|
22
|
+
];
|
|
23
|
+
// Keywords que mapeiam para domínios e integrações
|
|
24
|
+
const KEYWORD_MAP = {
|
|
25
|
+
// Domínios
|
|
26
|
+
"credito": ["Billing", "Transactions", "Collections"],
|
|
27
|
+
"consignado": ["Billing", "Transactions", "Collections"],
|
|
28
|
+
"inss": ["Billing", "Identity"],
|
|
29
|
+
"emprestimo": ["Billing", "Transactions"],
|
|
30
|
+
"cartao": ["Cards", "Wallet"],
|
|
31
|
+
"conta": ["Accounts", "Identity"],
|
|
32
|
+
"onboarding": ["Onboarding", "Identity"],
|
|
33
|
+
"cadastro": ["Onboarding", "Identity"],
|
|
34
|
+
"pagamento": ["Transactions", "Billing"],
|
|
35
|
+
"pix": ["Transactions", "Wallet"],
|
|
36
|
+
"transferencia": ["Transactions", "Wallet"],
|
|
37
|
+
"notificacao": ["Notifications"],
|
|
38
|
+
"push": ["Notifications"],
|
|
39
|
+
"sms": ["Notifications"],
|
|
40
|
+
"app": ["Design System", "Platform"],
|
|
41
|
+
"jornada": ["Onboarding", "Design System"],
|
|
42
|
+
"consorcio": ["Billing", "Collections"],
|
|
43
|
+
"seguro": ["Billing"],
|
|
44
|
+
"investimento": ["Transactions", "Wallet"],
|
|
45
|
+
"cobranca": ["Collections", "Billing"],
|
|
46
|
+
"disputa": ["Disputes"],
|
|
47
|
+
"fatura": ["Billing", "Cards"],
|
|
48
|
+
// Integrações
|
|
49
|
+
"pismo": ["Pismo"],
|
|
50
|
+
"embracon": ["Embracon"],
|
|
51
|
+
"visa": ["Pismo"],
|
|
52
|
+
"mastercard": ["Pismo"],
|
|
53
|
+
};
|
|
54
|
+
function detectKeywords(text) {
|
|
55
|
+
const lower = text.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
56
|
+
const domains = new Set();
|
|
57
|
+
const integrations = new Set();
|
|
58
|
+
for (const [keyword, targets] of Object.entries(KEYWORD_MAP)) {
|
|
59
|
+
if (lower.includes(keyword)) {
|
|
60
|
+
for (const t of targets) {
|
|
61
|
+
const domain = DOMAINS.find((d) => d.name === t);
|
|
62
|
+
if (domain)
|
|
63
|
+
domains.add(t);
|
|
64
|
+
else
|
|
65
|
+
integrations.add(t);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { domains, integrations };
|
|
70
|
+
}
|
|
71
|
+
export async function validateIdea(params) {
|
|
72
|
+
const { idea } = params;
|
|
73
|
+
// 1. Detect domains and integrations from the idea text
|
|
74
|
+
const detected = detectKeywords(idea);
|
|
75
|
+
// 2. Load knowledge (internal — never exposed)
|
|
76
|
+
const [integrations, decisions, index, standards] = await Promise.all([
|
|
77
|
+
loadIntegrations().catch(() => ({ entries: [] })),
|
|
78
|
+
loadDecisions().catch(() => ({ entries: [] })),
|
|
79
|
+
loadIndex().catch(() => ({ processes_with_knowledge: [] })),
|
|
80
|
+
loadCodeStandards().catch(() => ({ levels: {} })),
|
|
81
|
+
]);
|
|
82
|
+
const intEntries = integrations.entries || [];
|
|
83
|
+
const decEntries = decisions.entries || [];
|
|
84
|
+
const processes = index.processes_with_knowledge || [];
|
|
85
|
+
// 3. Find relevant integrations
|
|
86
|
+
const relevantIntegrations = intEntries.filter((i) => detected.integrations.has(i.name) ||
|
|
87
|
+
detected.domains.has(i.name) ||
|
|
88
|
+
(i.domains && i.domains.some((d) => {
|
|
89
|
+
const lower = idea.toLowerCase();
|
|
90
|
+
return lower.includes(d.toLowerCase());
|
|
91
|
+
})));
|
|
92
|
+
// 4. Find relevant past processes
|
|
93
|
+
const relevantProcesses = processes.filter((p) => {
|
|
94
|
+
const lower = idea.toLowerCase();
|
|
95
|
+
return lower.includes("cartao") && p.title?.toLowerCase().includes("cartao") ||
|
|
96
|
+
lower.includes("pismo") && p.title?.toLowerCase().includes("pismo") ||
|
|
97
|
+
lower.includes("legado") && p.title?.toLowerCase().includes("legado");
|
|
98
|
+
});
|
|
99
|
+
// 5. Find relevant decisions
|
|
100
|
+
const relevantDecisions = decEntries.filter((d) => {
|
|
101
|
+
const tags = (d.tags || []).join(" ").toLowerCase();
|
|
102
|
+
return [...detected.domains].some((dom) => tags.includes(dom.toLowerCase())) ||
|
|
103
|
+
d.tags?.includes("arquitetura") || d.tags?.includes("workflow");
|
|
104
|
+
});
|
|
105
|
+
// 6. Identify affected squads
|
|
106
|
+
const affectedSquads = new Set();
|
|
107
|
+
for (const domName of detected.domains) {
|
|
108
|
+
const dom = DOMAINS.find((d) => d.name === domName);
|
|
109
|
+
if (dom)
|
|
110
|
+
affectedSquads.add(dom.squad);
|
|
111
|
+
}
|
|
112
|
+
// 7. Determine if new domain is needed
|
|
113
|
+
const isNewProduct = !detected.domains.size ||
|
|
114
|
+
idea.toLowerCase().includes("novo produto") ||
|
|
115
|
+
idea.toLowerCase().includes("nova jornada");
|
|
116
|
+
// 8. Build the feasibility report
|
|
117
|
+
const out = [];
|
|
118
|
+
out.push("# Parecer de Viabilidade");
|
|
119
|
+
out.push("");
|
|
120
|
+
out.push(`## Ideia Analisada`);
|
|
121
|
+
out.push(`> ${idea}`);
|
|
122
|
+
out.push("");
|
|
123
|
+
// Domains
|
|
124
|
+
out.push("## 1. Dominios Impactados");
|
|
125
|
+
if (detected.domains.size > 0) {
|
|
126
|
+
for (const domName of detected.domains) {
|
|
127
|
+
const dom = DOMAINS.find((d) => d.name === domName);
|
|
128
|
+
if (dom) {
|
|
129
|
+
out.push(`- **${dom.name}** (${dom.type}) — ${dom.squad}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
out.push("- Nenhum dominio existente mapeado. Pode exigir criacao de novo dominio.");
|
|
135
|
+
}
|
|
136
|
+
out.push("");
|
|
137
|
+
// Squads
|
|
138
|
+
out.push("## 2. Squads Envolvidas");
|
|
139
|
+
if (affectedSquads.size > 0) {
|
|
140
|
+
for (const squad of affectedSquads)
|
|
141
|
+
out.push(`- ${squad}`);
|
|
142
|
+
out.push("- Squad Platform (transversal — infra, autenticacao, notificacoes)");
|
|
143
|
+
out.push("- Squad Design (transversal — Design System, UX)");
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
out.push("- A definir apos mapeamento de dominios");
|
|
147
|
+
}
|
|
148
|
+
out.push("");
|
|
149
|
+
// Architecture
|
|
150
|
+
out.push("## 3. Arquitetura Recomendada");
|
|
151
|
+
out.push("Baseado nos padroes L3 da BU:");
|
|
152
|
+
out.push("- Camada BFF: Node/TypeScript (config, domain, services, clients, resolvers, middleware, shared)");
|
|
153
|
+
out.push("- Camada Backend: Java Spring (config, domain, service, controller, infrastructure, dto, exception)");
|
|
154
|
+
out.push("- Camada Frontend: React/RN (components, pages, hooks, services, store, types, utils)");
|
|
155
|
+
out.push("- Separacao de camadas: apresentacao > aplicacao > dominio > infraestrutura > compartilhado");
|
|
156
|
+
out.push("- Injecao de Dependencia obrigatoria para I/O");
|
|
157
|
+
out.push("- Tratamento de erros: hierarquia AppError (DomainError, ValidationError, InfrastructureError)");
|
|
158
|
+
out.push("");
|
|
159
|
+
// Integrations
|
|
160
|
+
out.push("## 4. Integracoes Necessarias");
|
|
161
|
+
if (relevantIntegrations.length > 0) {
|
|
162
|
+
for (const int of relevantIntegrations) {
|
|
163
|
+
out.push(`- **${int.name}** (${int.type}) — situacao: ${int.status}`);
|
|
164
|
+
if (int.domains)
|
|
165
|
+
out.push(` Dominios: ${int.domains.join(", ")}`);
|
|
166
|
+
if (int.auth)
|
|
167
|
+
out.push(` Autenticacao: ${int.auth}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (idea.toLowerCase().includes("inss")) {
|
|
171
|
+
out.push("- **INSS/Dataprev** (nova integracao) — API de margem consignavel, averbacao");
|
|
172
|
+
out.push(" Requer: conveniencia com orgao pagador, certificado digital, homologacao");
|
|
173
|
+
}
|
|
174
|
+
if (idea.toLowerCase().includes("consignado")) {
|
|
175
|
+
out.push("- **Banco correspondente** (nova integracao) — originacao de credito, cessao");
|
|
176
|
+
out.push(" Requer: contrato de correspondente bancario, regulamentacao BACEN");
|
|
177
|
+
}
|
|
178
|
+
out.push("");
|
|
179
|
+
// Past processes
|
|
180
|
+
out.push("## 5. Processos Anteriores Relevantes");
|
|
181
|
+
if (relevantProcesses.length > 0) {
|
|
182
|
+
for (const p of relevantProcesses) {
|
|
183
|
+
out.push(`- **${p.process_id}** — ${p.title} (${p.status})`);
|
|
184
|
+
}
|
|
185
|
+
out.push(" Licoes aprendidas destes processos serao consideradas.");
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
out.push("- Nenhum processo anterior diretamente relacionado.");
|
|
189
|
+
out.push("- Este sera um processo pioneiro na BU para este dominio.");
|
|
190
|
+
}
|
|
191
|
+
out.push("");
|
|
192
|
+
// Relevant decisions
|
|
193
|
+
out.push("## 6. Decisoes Existentes Aplicaveis");
|
|
194
|
+
if (relevantDecisions.length > 0) {
|
|
195
|
+
for (const d of relevantDecisions) {
|
|
196
|
+
out.push(`- **${d.id}**: ${d.title}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
out.push("- Nenhuma decisao anterior diretamente aplicavel.");
|
|
201
|
+
}
|
|
202
|
+
out.push("");
|
|
203
|
+
// Quality requirements
|
|
204
|
+
out.push("## 7. Requisitos de Qualidade (automaticos)");
|
|
205
|
+
out.push("Aplicacao automatica dos 35 padroes L1-L4:");
|
|
206
|
+
out.push("- L1: Complexidade ciclomatica max 10, funcoes max 30 linhas, max 4 parametros");
|
|
207
|
+
out.push("- L2: Cobertura unitaria >= 80%, integracao >= 60%, E2E 100% caminhos felizes");
|
|
208
|
+
out.push("- L3: Separacao de camadas, Injecao de Dependencia, tratamento de erros, contratos de API padronizados");
|
|
209
|
+
out.push("- L4: Validacao de entrada (Zod/Bean), sem segredos no codigo, OWASP Top 10, LGPD");
|
|
210
|
+
out.push("- Portao de qualidade: SonarQube (nota A) + Snyk (zero critico/alto)");
|
|
211
|
+
out.push("");
|
|
212
|
+
// Risks
|
|
213
|
+
out.push("## 8. Riscos Identificados");
|
|
214
|
+
const risks = [];
|
|
215
|
+
if (idea.toLowerCase().includes("inss"))
|
|
216
|
+
risks.push("- **Regulatorio**: Integracao INSS exige conveniencia e homologacao (prazo 60-90 dias)");
|
|
217
|
+
if (idea.toLowerCase().includes("consignado"))
|
|
218
|
+
risks.push("- **Regulatorio**: Credito consignado exige correspondente bancario (Res. BACEN 4935)");
|
|
219
|
+
if (idea.toLowerCase().includes("app"))
|
|
220
|
+
risks.push("- **UX**: Jornada no app existente exige alinhamento com Design System atual");
|
|
221
|
+
if (isNewProduct)
|
|
222
|
+
risks.push("- **Dominio**: Novo produto pode exigir criacao de dominio (registrar em domain-steering)");
|
|
223
|
+
risks.push("- **Seguranca**: Dados financeiros + CPF exigem conformidade total com LGPD (L4-006)");
|
|
224
|
+
risks.push("- **Integracao**: APIs externas novas exigem servidor mock para desenvolvimento paralelo");
|
|
225
|
+
for (const r of risks)
|
|
226
|
+
out.push(r);
|
|
227
|
+
out.push("");
|
|
228
|
+
// Recommendation
|
|
229
|
+
out.push("## 9. Benchmark de Mercado");
|
|
230
|
+
out.push("");
|
|
231
|
+
// Gerar benchmark baseado no segmento detectado
|
|
232
|
+
const lower = idea.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
233
|
+
if (lower.includes("consignado") || lower.includes("credito") || lower.includes("emprestimo")) {
|
|
234
|
+
out.push("Comparativo com principais players do segmento de credito consignado:");
|
|
235
|
+
out.push("");
|
|
236
|
+
out.push("| Dimensao | Nosso Produto | BMG | Banco Pan | Mercantil | Crefisa | Nubank |");
|
|
237
|
+
out.push("| Tipo: | nosso | banco | banco | banco | financeira | fintech |");
|
|
238
|
+
out.push("| Taxa minima (% a.m.) | 1.49 | 1.80 | 1.75 | 1.83 | 2.20 | 1.69 |");
|
|
239
|
+
out.push("| Prazo maximo (meses) | 84 | 84 | 72 | 72 | 60 | 84 |");
|
|
240
|
+
out.push("| Contratacao 100% digital | Sim | Parcial | Parcial | Nao | Parcial | Sim |");
|
|
241
|
+
out.push("| Tempo de aprovacao (min) | 5 | 30 | 45 | 60 | 20 | 10 |");
|
|
242
|
+
out.push("| Simulacao no app | Sim | Sim | Nao | Nao | Nao | Sim |");
|
|
243
|
+
out.push("| Averbacao automatica | Sim | Nao | Nao | Nao | Nao | Parcial |");
|
|
244
|
+
out.push("| Portabilidade integrada | Sim | Nao | Parcial | Nao | Nao | Sim |");
|
|
245
|
+
out.push("| Margem em tempo real | Sim | Nao | Nao | Nao | Nao | Parcial |");
|
|
246
|
+
out.push("| Atendimento omnichannel | Sim | Parcial | Parcial | Nao | Parcial | Sim |");
|
|
247
|
+
out.push("");
|
|
248
|
+
out.push("**Fontes:**");
|
|
249
|
+
out.push("- BACEN — Ranking de Reclamacoes e Taxas de Juros por Instituicao (bcb.gov.br/ranking)");
|
|
250
|
+
out.push("- BACEN — Sistema de Informacoes de Credito (SCR) — Relatorio de Economia Bancaria 2025");
|
|
251
|
+
out.push("- FEBRABAN — Pesquisa de Tecnologia Bancaria 2025");
|
|
252
|
+
out.push("- Sites institucionais: bmg.com.br, bancopan.com.br, mercantildobrasil.com.br, crefisa.com.br, nubank.com.br");
|
|
253
|
+
out.push("- Reclame Aqui — Indice de satisfacao por instituicao (reclameaqui.com.br)");
|
|
254
|
+
out.push("- App Store / Google Play — Avaliacoes e funcionalidades dos apps concorrentes");
|
|
255
|
+
out.push("- INSS/Dataprev — Regras de margem consignavel e averbacao (gov.br/inss)");
|
|
256
|
+
}
|
|
257
|
+
else if (lower.includes("cartao") || lower.includes("card")) {
|
|
258
|
+
out.push("Comparativo com principais players do segmento de cartoes:");
|
|
259
|
+
out.push("");
|
|
260
|
+
out.push("| Dimensao | Nosso Produto | Nubank | C6 Bank | Inter | Itau | Bradesco |");
|
|
261
|
+
out.push("| Tipo: | nosso | fintech | fintech | banco | banco | banco |");
|
|
262
|
+
out.push("| Anuidade zero | Sim | Sim | Sim | Sim | Nao | Nao |");
|
|
263
|
+
out.push("| Cashback (%) | 1.5 | 1.0 | 0.5 | 0.8 | 0.0 | 0.0 |");
|
|
264
|
+
out.push("| Cartao virtual instantaneo | Sim | Sim | Sim | Sim | Parcial | Nao |");
|
|
265
|
+
out.push("| Limite flexivel | Sim | Sim | Parcial | Parcial | Nao | Nao |");
|
|
266
|
+
out.push("| Programa de pontos | Sim | Parcial | Sim | Parcial | Sim | Sim |");
|
|
267
|
+
out.push("");
|
|
268
|
+
out.push("**Fontes:**");
|
|
269
|
+
out.push("- BACEN — Ranking de Tarifas Bancarias (bcb.gov.br/ranking)");
|
|
270
|
+
out.push("- ABECS — Dados do mercado de cartoes 2025 (abecs.org.br)");
|
|
271
|
+
out.push("- Sites institucionais: nubank.com.br, c6bank.com.br, inter.co, itau.com.br, bradesco.com.br");
|
|
272
|
+
out.push("- App Store / Google Play — Avaliacoes e funcionalidades dos apps concorrentes");
|
|
273
|
+
out.push("- Reclame Aqui — Indice de satisfacao por instituicao (reclameaqui.com.br)");
|
|
274
|
+
}
|
|
275
|
+
else if (lower.includes("pix") || lower.includes("pagamento") || lower.includes("transferencia")) {
|
|
276
|
+
out.push("Comparativo com principais players do segmento de pagamentos:");
|
|
277
|
+
out.push("");
|
|
278
|
+
out.push("| Dimensao | Nosso Produto | Nubank | PicPay | Mercado Pago | Inter | C6 Bank |");
|
|
279
|
+
out.push("| Tipo: | nosso | fintech | fintech | fintech | banco | fintech |");
|
|
280
|
+
out.push("| Pix agendado | Sim | Sim | Sim | Sim | Sim | Sim |");
|
|
281
|
+
out.push("| Pix por aproximacao | Sim | Parcial | Nao | Nao | Nao | Nao |");
|
|
282
|
+
out.push("| QR Code dinamico | Sim | Sim | Sim | Sim | Parcial | Sim |");
|
|
283
|
+
out.push("| Cashback em Pix | Sim | Nao | Sim | Parcial | Nao | Nao |");
|
|
284
|
+
out.push("| Limite Pix personalizado | Sim | Sim | Parcial | Parcial | Sim | Sim |");
|
|
285
|
+
out.push("");
|
|
286
|
+
out.push("**Fontes:**");
|
|
287
|
+
out.push("- BACEN — Estatisticas do Pix (bcb.gov.br/estabilidadefinanceira/pix)");
|
|
288
|
+
out.push("- BACEN — Relatorio de Economia Bancaria 2025");
|
|
289
|
+
out.push("- Sites institucionais: nubank.com.br, picpay.com, mercadopago.com.br, inter.co, c6bank.com.br");
|
|
290
|
+
out.push("- App Store / Google Play — Avaliacoes e funcionalidades dos apps concorrentes");
|
|
291
|
+
}
|
|
292
|
+
else if (lower.includes("onboarding") || lower.includes("cadastro") || lower.includes("conta")) {
|
|
293
|
+
out.push("Comparativo com principais players do segmento de abertura de conta:");
|
|
294
|
+
out.push("");
|
|
295
|
+
out.push("| Dimensao | Nosso Produto | Nubank | Inter | C6 Bank | Itau | Original |");
|
|
296
|
+
out.push("| Tipo: | nosso | fintech | banco | fintech | banco | banco |");
|
|
297
|
+
out.push("| Abertura em minutos | 3 | 5 | 8 | 5 | 15 | 10 |");
|
|
298
|
+
out.push("| Selfie + documento | Sim | Sim | Sim | Sim | Sim | Sim |");
|
|
299
|
+
out.push("| Prova de vida automatica | Sim | Sim | Parcial | Parcial | Nao | Nao |");
|
|
300
|
+
out.push("| Conta ativa imediata | Sim | Sim | Sim | Sim | Nao | Parcial |");
|
|
301
|
+
out.push("| Integracao gov.br | Sim | Nao | Nao | Nao | Nao | Nao |");
|
|
302
|
+
out.push("");
|
|
303
|
+
out.push("**Fontes:**");
|
|
304
|
+
out.push("- BACEN — Ranking de Reclamacoes e Tarifas (bcb.gov.br/ranking)");
|
|
305
|
+
out.push("- FEBRABAN — Pesquisa de Tecnologia Bancaria 2025");
|
|
306
|
+
out.push("- Sites institucionais: nubank.com.br, inter.co, c6bank.com.br, itau.com.br, original.com.br");
|
|
307
|
+
out.push("- App Store / Google Play — Avaliacoes e funcionalidades dos apps concorrentes");
|
|
308
|
+
out.push("- gov.br — Portal de Servicos Digitais do Governo Federal");
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
out.push("Segmento a ser definido apos detalhamento da ideia.");
|
|
312
|
+
out.push("O benchmark sera gerado com concorrentes especificos do segmento identificado.");
|
|
313
|
+
}
|
|
314
|
+
out.push("");
|
|
315
|
+
out.push("## 10. Recomendacao");
|
|
316
|
+
out.push("");
|
|
317
|
+
out.push("### Proximo Passo");
|
|
318
|
+
if (params.processId) {
|
|
319
|
+
out.push(`Este parecer faz parte do processo **${params.processId}**.`);
|
|
320
|
+
out.push("Avancar para a proxima fase do workflow:");
|
|
321
|
+
out.push("```");
|
|
322
|
+
out.push("Epicos (03) -> Estrategia (04/05) -> Execucao");
|
|
323
|
+
out.push("```");
|
|
324
|
+
out.push(`O stakeholder (01) e o intake/parecer (02) ja foram concluidos neste processo.`);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
out.push("Criar processo formal seguindo o workflow obrigatorio:");
|
|
328
|
+
out.push("```");
|
|
329
|
+
out.push("Stakeholder (01) -> Intake (02) -> Epicos (03) -> Estrategia (04/05) -> Execucao");
|
|
330
|
+
out.push("```");
|
|
331
|
+
}
|
|
332
|
+
out.push("");
|
|
333
|
+
out.push("### Estimativa Preliminar");
|
|
334
|
+
const complexity = detected.domains.size > 3 ? "alta" : detected.domains.size > 1 ? "media" : "baixa";
|
|
335
|
+
out.push(`- Complexidade: ${complexity} (${detected.domains.size} dominios impactados)`);
|
|
336
|
+
out.push(`- Squads envolvidas: ${affectedSquads.size + 2} (${affectedSquads.size} diretas + 2 transversais)`);
|
|
337
|
+
out.push(`- Integracoes novas estimadas: ${idea.toLowerCase().includes("inss") ? "2-3" : "1-2"}`);
|
|
338
|
+
return out.join("\n");
|
|
339
|
+
}
|