agent-master-devsecops 3.9.0 → 5.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/README.md +205 -371
- package/agents/AGENT_DEVSECOPS.md +32 -417
- package/agents/AGENT_INFRA.md +31 -0
- package/agents/AGENT_ORCHESTRATOR.md +39 -264
- package/agents/AGENT_QA.md +58 -186
- package/agents/AGENT_SCRUM.md +23 -190
- package/agents/AGENT_SRE.md +22 -173
- package/agents/AGENT_UIUX.md +26 -162
- package/docs/ARCHITECTURE.md +152 -148
- package/docs/CALCULATIONS.md +17 -17
- package/docs/CHANGELOG.md +112 -49
- package/docs/ENGINEERING_RULES.md +56 -252
- package/docs/IDEAS.md +17 -17
- package/docs/LOCAL_BACKLOG.md +20 -0
- package/docs/STATE.md +111 -43
- package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -58
- package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -58
- package/package.json +59 -47
- package/scripts/auditors/a11y_auditor.js +462 -0
- package/scripts/auditors/code_quality_auditor.js +444 -0
- package/scripts/auditors/contract_test_validator.js +396 -0
- package/scripts/auditors/eval_harness.js +213 -0
- package/scripts/auditors/git_hygiene_linter.js +391 -0
- package/scripts/auditors/pr_reviewer.js +627 -0
- package/scripts/auditors/sast_scanner.js +442 -0
- package/scripts/auditors/sca_dependency_auditor.js +673 -0
- package/scripts/{bootstrap_cli.js → bin/bootstrap_cli.js} +597 -468
- package/scripts/bin/domain_idea_miner.js +658 -0
- package/scripts/bin/smart_onboarding.js +905 -0
- package/scripts/{web_inspector.js → bin/web_inspector.js} +578 -355
- package/scripts/core/architecture_advisor.js +378 -0
- package/scripts/core/ast_skeletonizer.js +413 -0
- package/scripts/core/autonomous_remediation_engine.js +305 -0
- package/scripts/{circuit_breaker.js → core/circuit_breaker.js} +546 -330
- package/scripts/core/context_compressor.js +237 -0
- package/scripts/core/fast_feature_scaffold.js +840 -0
- package/scripts/core/hot_state_cache.js +238 -0
- package/scripts/core/knowledge_gateway.js +888 -0
- package/scripts/{magic_prompt_generator.js → core/magic_prompt_generator.js} +250 -205
- package/scripts/{multi_agent_orchestrator.js → core/multi_agent_orchestrator.js} +421 -258
- package/scripts/{otel_tracer.js → core/otel_tracer.js} +335 -228
- package/scripts/{perf_benchmark.js → core/perf_benchmark.js} +212 -153
- package/scripts/core/sarif_engine.js +299 -0
- package/scripts/{sre_remediation_engine.js → core/sre_remediation_engine.js} +236 -174
- package/scripts/{universal_packager.js → core/universal_packager.js} +357 -313
- package/scripts/integrations/atlassian_client.js +600 -0
- package/scripts/integrations/atlassian_sync_action.js +576 -0
- package/scripts/integrations/confluence_sync.js +684 -0
- package/scripts/integrations/credentials_guard.js +500 -0
- package/scripts/integrations/jira_manager.js +722 -0
- package/scripts/mcp/handlers/atlassian_handlers.js +300 -0
- package/scripts/mcp/handlers/core_handlers.js +419 -0
- package/scripts/mcp/handlers/qa_handlers.js +502 -0
- package/scripts/mcp/handlers/rules_handlers.js +379 -0
- package/scripts/mcp/handlers/scaffold_handlers.js +334 -0
- package/scripts/mcp/handlers/sre_handlers.js +443 -0
- package/scripts/mcp/resources_registry.js +453 -0
- package/scripts/mcp/server.js +408 -0
- package/scripts/mcp/tool_handlers.js +444 -0
- package/scripts/mcp/tools_registry.js +1123 -0
- package/scripts/mcp_server.js +236 -1884
- package/.cursorignore +0 -23
- package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +0 -53
- package/scripts/a11y_auditor.js +0 -187
- package/scripts/ast_skeletonizer.js +0 -220
- package/scripts/atlassian_client.js +0 -238
- package/scripts/atlassian_sync_action.js +0 -269
- package/scripts/code_quality_auditor.js +0 -142
- package/scripts/confluence_sync.js +0 -337
- package/scripts/context_compressor.js +0 -139
- package/scripts/contract_test_validator.js +0 -166
- package/scripts/credentials_guard.js +0 -216
- package/scripts/domain_idea_miner.js +0 -364
- package/scripts/eval_harness.js +0 -100
- package/scripts/fast_feature_scaffold.js +0 -338
- package/scripts/git_hygiene_linter.js +0 -186
- package/scripts/hot_state_cache.js +0 -95
- package/scripts/jira_manager.js +0 -408
- package/scripts/pr_reviewer.js +0 -373
- package/scripts/sast_scanner.js +0 -180
- package/scripts/sca_dependency_auditor.js +0 -167
- package/scripts/smart_onboarding.js +0 -610
|
@@ -1,610 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scripts/smart_onboarding.js
|
|
4
|
-
*
|
|
5
|
-
* Motor de Onboarding Bimodal Autônomo & Auto-Provisionamento da Tríade (Regra 16 - AGDEV-37).
|
|
6
|
-
* Detecta automaticamente se o projeto é Greenfield (novo do zero) ou Brownfield (existente),
|
|
7
|
-
* audita credenciais Atlassian, auto-provisiona Projeto no Jira e Espaço no Confluence via API (se ausentes),
|
|
8
|
-
* e publica um Portal Executivo de Arquitetura no Confluence sem jamais deixar a Homepage vazia.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
|
-
const fs = require('node:fs');
|
|
14
|
-
const path = require('node:path');
|
|
15
|
-
const { getAtlassianCredentials, requestAtlassian } = require('./atlassian_client.js');
|
|
16
|
-
|
|
17
|
-
const IGNORED_DIRS = new Set([
|
|
18
|
-
'.git',
|
|
19
|
-
'.gemini',
|
|
20
|
-
'.antigravity',
|
|
21
|
-
'.vscode',
|
|
22
|
-
'.idea',
|
|
23
|
-
'node_modules',
|
|
24
|
-
'agent-master-devsecops'
|
|
25
|
-
]);
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 1. Detecção Bimodal: Greenfield vs. Brownfield
|
|
29
|
-
*/
|
|
30
|
-
function detectProjectMode(targetDir = process.cwd()) {
|
|
31
|
-
if (!fs.existsSync(targetDir)) {
|
|
32
|
-
return {
|
|
33
|
-
mode: 'GREENFIELD',
|
|
34
|
-
isBrownfield: false,
|
|
35
|
-
isGreenfield: true,
|
|
36
|
-
targetDir,
|
|
37
|
-
evidence: { foundManifests: [], foundSourceDirs: [], hasReadme: false, totalRelevantEntries: 0 }
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
const allEntries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
41
|
-
const relevantEntries = allEntries.filter(e => !IGNORED_DIRS.has(e.name));
|
|
42
|
-
|
|
43
|
-
const buildManifests = [
|
|
44
|
-
'pom.xml', 'build.gradle', 'package.json', 'go.mod', 'Cargo.toml',
|
|
45
|
-
'requirements.txt', 'pyproject.toml', 'Gemfile', 'composer.json',
|
|
46
|
-
'Dockerfile', 'docker-compose.yml', 'Makefile'
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
const foundManifests = [];
|
|
50
|
-
const foundSourceDirs = [];
|
|
51
|
-
let hasReadme = false;
|
|
52
|
-
|
|
53
|
-
for (const entry of relevantEntries) {
|
|
54
|
-
if (entry.isFile()) {
|
|
55
|
-
if (buildManifests.includes(entry.name)) {
|
|
56
|
-
foundManifests.push(entry.name);
|
|
57
|
-
}
|
|
58
|
-
if (entry.name.toLowerCase() === 'readme.md') {
|
|
59
|
-
hasReadme = true;
|
|
60
|
-
}
|
|
61
|
-
} else if (entry.isDirectory()) {
|
|
62
|
-
const subEntries = fs.readdirSync(path.join(targetDir, entry.name)).filter(f => !IGNORED_DIRS.has(f));
|
|
63
|
-
if (subEntries.length > 0) {
|
|
64
|
-
foundSourceDirs.push(entry.name);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const isBrownfield = foundManifests.length > 0 || foundSourceDirs.length > 0 || (hasReadme && relevantEntries.length > 1);
|
|
70
|
-
const mode = isBrownfield ? 'BROWNFIELD' : 'GREENFIELD';
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
mode,
|
|
74
|
-
isBrownfield,
|
|
75
|
-
isGreenfield: !isBrownfield,
|
|
76
|
-
targetDir,
|
|
77
|
-
evidence: {
|
|
78
|
-
foundManifests,
|
|
79
|
-
foundSourceDirs,
|
|
80
|
-
hasReadme,
|
|
81
|
-
totalRelevantEntries: relevantEntries.length
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 2. Engenharia Reversa Semântica de Projetos Brownfield
|
|
88
|
-
*/
|
|
89
|
-
function inspectProjectContext(targetDir = process.cwd()) {
|
|
90
|
-
if (!fs.existsSync(targetDir)) {
|
|
91
|
-
return {
|
|
92
|
-
mode: 'GREENFIELD',
|
|
93
|
-
name: path.basename(path.resolve(targetDir)),
|
|
94
|
-
title: path.basename(path.resolve(targetDir)).toUpperCase(),
|
|
95
|
-
description: 'Novo projeto Greenfield em inicialização.',
|
|
96
|
-
businessDomain: 'Geral',
|
|
97
|
-
stack: ['Agnóstico / Em definição'],
|
|
98
|
-
modules: [],
|
|
99
|
-
entryPoints: [],
|
|
100
|
-
hasDocker: false
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
const detection = detectProjectMode(targetDir);
|
|
104
|
-
const info = {
|
|
105
|
-
mode: detection.mode,
|
|
106
|
-
name: path.basename(path.resolve(targetDir)),
|
|
107
|
-
title: '',
|
|
108
|
-
description: '',
|
|
109
|
-
businessDomain: 'Geral / Engenharia de Software',
|
|
110
|
-
stack: [],
|
|
111
|
-
modules: [],
|
|
112
|
-
entryPoints: [],
|
|
113
|
-
hasDocker: false
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Leitura do README
|
|
117
|
-
const readmePath = path.join(targetDir, 'README.md');
|
|
118
|
-
if (fs.existsSync(readmePath)) {
|
|
119
|
-
try {
|
|
120
|
-
const readmeText = fs.readFileSync(readmePath, 'utf8');
|
|
121
|
-
const lines = readmeText.split(/\r?\n/).filter(Boolean);
|
|
122
|
-
for (const line of lines) {
|
|
123
|
-
if (!info.title && line.startsWith('# ')) {
|
|
124
|
-
info.title = line.replace(/^#\s+/, '').trim();
|
|
125
|
-
} else if (!info.description && !line.startsWith('#') && !line.startsWith('>')) {
|
|
126
|
-
info.description = line.trim();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} catch (_) {}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (!info.title) info.title = info.name.replace(/[-_]/g, ' ').toUpperCase();
|
|
133
|
-
if (!info.description) info.description = `Ecossistema de software governado pelo Agent Master DevSecOps.`;
|
|
134
|
-
|
|
135
|
-
// Mapeamento de Stack e Módulos
|
|
136
|
-
const entries = fs.readdirSync(targetDir, { withFileTypes: true }).filter(e => !IGNORED_DIRS.has(e.name));
|
|
137
|
-
for (const entry of entries) {
|
|
138
|
-
const fullPath = path.join(targetDir, entry.name);
|
|
139
|
-
if (entry.isDirectory()) {
|
|
140
|
-
const subFiles = fs.readdirSync(fullPath);
|
|
141
|
-
const isModule = subFiles.some(f => ['pom.xml', 'package.json', 'Dockerfile', 'src'].includes(f));
|
|
142
|
-
if (isModule) {
|
|
143
|
-
info.modules.push({
|
|
144
|
-
name: entry.name,
|
|
145
|
-
hasDocker: subFiles.includes('Dockerfile'),
|
|
146
|
-
hasMaven: subFiles.includes('pom.xml'),
|
|
147
|
-
hasNpm: subFiles.includes('package.json')
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
} else if (entry.isFile()) {
|
|
151
|
-
if (entry.name === 'pom.xml') info.stack.push('Java (Maven / Spring Boot)');
|
|
152
|
-
if (entry.name === 'package.json') info.stack.push('Node.js / TypeScript');
|
|
153
|
-
if (entry.name === 'go.mod') info.stack.push('Go');
|
|
154
|
-
if (entry.name === 'Cargo.toml') info.stack.push('Rust');
|
|
155
|
-
if (entry.name === 'Dockerfile') {
|
|
156
|
-
info.hasDocker = true;
|
|
157
|
-
info.stack.push('Docker');
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (info.modules.some(m => m.hasMaven)) info.stack.push('Java / Spring Boot');
|
|
163
|
-
if (info.modules.some(m => m.hasNpm)) info.stack.push('Node.js');
|
|
164
|
-
if (info.modules.some(m => m.hasDocker)) info.hasDocker = true;
|
|
165
|
-
|
|
166
|
-
info.stack = Array.from(new Set(info.stack));
|
|
167
|
-
if (info.stack.length === 0) info.stack.push('Agnóstico / Em definição');
|
|
168
|
-
|
|
169
|
-
return info;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 3. Auto-Provisionamento do Projeto no Jira
|
|
174
|
-
*/
|
|
175
|
-
async function ensureJiraProject(creds, projectKey, projectName = 'DevSecOps Project') {
|
|
176
|
-
if (!creds || !creds.isConfigured) {
|
|
177
|
-
return { status: 'SKIPPED', reason: 'Credenciais Atlassian ausentes ou incompletas.' };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const cleanKey = String(projectKey).toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 10);
|
|
181
|
-
|
|
182
|
-
// 1. Verifica se já existe
|
|
183
|
-
const checkRes = await requestAtlassian(creds, 'GET', `/rest/api/3/project/${cleanKey}`);
|
|
184
|
-
if (checkRes.status === 200) {
|
|
185
|
-
return {
|
|
186
|
-
status: 'EXISTS',
|
|
187
|
-
created: false,
|
|
188
|
-
key: cleanKey,
|
|
189
|
-
id: checkRes.body?.id,
|
|
190
|
-
name: checkRes.body?.name
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// 2. Se não existir (404), obtém o accountId do líder
|
|
195
|
-
const myselfRes = await requestAtlassian(creds, 'GET', '/rest/api/3/myself');
|
|
196
|
-
const leadAccountId = myselfRes.body?.accountId;
|
|
197
|
-
|
|
198
|
-
if (!leadAccountId) {
|
|
199
|
-
return { status: 'FAILED', reason: 'Não foi possível recuperar o accountId do usuário líder.' };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const payload = {
|
|
203
|
-
key: cleanKey,
|
|
204
|
-
name: projectName,
|
|
205
|
-
projectTypeKey: 'software',
|
|
206
|
-
projectTemplateKey: 'com.pyxis.greenhopper.jira:gh-simplified-scrum-classic',
|
|
207
|
-
description: `Projeto ${projectName} criado automaticamente pelo Agent Master DevSecOps (Regra 16).`,
|
|
208
|
-
leadAccountId,
|
|
209
|
-
assigneeType: 'PROJECT_LEAD'
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const createRes = await requestAtlassian(creds, 'POST', '/rest/api/3/project', payload);
|
|
213
|
-
if (createRes.status === 200 || createRes.status === 201) {
|
|
214
|
-
return {
|
|
215
|
-
status: 'CREATED',
|
|
216
|
-
created: true,
|
|
217
|
-
key: cleanKey,
|
|
218
|
-
id: createRes.body?.id,
|
|
219
|
-
name: projectName
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return { status: 'FAILED', httpStatus: createRes.status, error: createRes.body };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* 4. Auto-Provisionamento do Espaço no Confluence
|
|
228
|
-
*/
|
|
229
|
-
async function ensureConfluenceSpace(creds, spaceKey, spaceName = 'DevSecOps Documentation') {
|
|
230
|
-
if (!creds || !creds.isConfigured) {
|
|
231
|
-
return { status: 'SKIPPED', reason: 'Credenciais Atlassian ausentes ou incompletas.' };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const cleanKey = String(spaceKey).toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 10);
|
|
235
|
-
|
|
236
|
-
// 1. Verifica se já existe
|
|
237
|
-
const checkRes = await requestAtlassian(creds, 'GET', `/wiki/rest/api/space/${cleanKey}`);
|
|
238
|
-
if (checkRes.status === 200) {
|
|
239
|
-
const hpId = checkRes.body?.homepage?.id || checkRes.body?.homepageId;
|
|
240
|
-
return {
|
|
241
|
-
status: 'EXISTS',
|
|
242
|
-
created: false,
|
|
243
|
-
key: cleanKey,
|
|
244
|
-
name: checkRes.body?.name,
|
|
245
|
-
homepageId: hpId
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// 2. Se não existir, cria o espaço
|
|
250
|
-
const payload = {
|
|
251
|
-
key: cleanKey,
|
|
252
|
-
name: spaceName,
|
|
253
|
-
description: {
|
|
254
|
-
plain: {
|
|
255
|
-
value: `Espaço de Documentação Técnica e Arquitetura do projeto ${spaceName} governado pelo Agent Master DevSecOps.`,
|
|
256
|
-
representation: 'plain'
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const createRes = await requestAtlassian(creds, 'POST', '/wiki/rest/api/space', payload);
|
|
262
|
-
if (createRes.status === 200 || createRes.status === 201) {
|
|
263
|
-
const hpId = createRes.body?.homepage?.id || createRes.body?.homepageId;
|
|
264
|
-
return {
|
|
265
|
-
status: 'CREATED',
|
|
266
|
-
created: true,
|
|
267
|
-
key: cleanKey,
|
|
268
|
-
name: spaceName,
|
|
269
|
-
homepageId: hpId
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return { status: 'FAILED', httpStatus: createRes.status, error: createRes.body };
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* 5. Publicação do Portal Executivo na Homepage (Zero Páginas Vazias)
|
|
278
|
-
*/
|
|
279
|
-
async function publishExecutiveHomepage(creds, spaceKey, homepageId, projectContext) {
|
|
280
|
-
if (!creds || !creds.isConfigured || !homepageId) {
|
|
281
|
-
return { status: 'SKIPPED', reason: 'Homepage ID ou credenciais ausentes.' };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Recupera versão atual
|
|
285
|
-
const getRes = await requestAtlassian(creds, 'GET', `/wiki/rest/api/content/${homepageId}?expand=version`);
|
|
286
|
-
const currentVersion = getRes.body?.version?.number || 1;
|
|
287
|
-
const nextVersion = currentVersion + 1;
|
|
288
|
-
|
|
289
|
-
const stackHtml = projectContext.stack.map(s => `<li>${s}</li>`).join('');
|
|
290
|
-
const modulesHtml = projectContext.modules.length > 0
|
|
291
|
-
? projectContext.modules.map(m => `<tr><td><code>${m.name}</code></td><td>${m.hasDocker ? 'Sim (Container)' : 'Aplicação'}</td><td>${m.hasMaven ? 'Maven' : (m.hasNpm ? 'NPM' : 'Nativo')}</td></tr>`).join('')
|
|
292
|
-
: `<tr><td><code>raiz</code></td><td>Aplicação Principal</td><td>Nativo</td></tr>`;
|
|
293
|
-
|
|
294
|
-
const html = `
|
|
295
|
-
<h2>🏛️ Portal de Engenharia & Arquitetura — ${projectContext.title}</h2>
|
|
296
|
-
<p><strong>Status de Operação:</strong> Homologado sob Governança Multi-Agente Prescritiva (Regra 16).</p>
|
|
297
|
-
<p>${projectContext.description}</p>
|
|
298
|
-
<hr />
|
|
299
|
-
<h3>📌 Estrutura Canônica de Documentação (Tríade Viva)</h3>
|
|
300
|
-
<table>
|
|
301
|
-
<thead>
|
|
302
|
-
<tr>
|
|
303
|
-
<th>Seção de Documentação</th>
|
|
304
|
-
<th>Propósito de Governança</th>
|
|
305
|
-
<th>Padrão de Registro</th>
|
|
306
|
-
</tr>
|
|
307
|
-
</thead>
|
|
308
|
-
<tbody>
|
|
309
|
-
<tr>
|
|
310
|
-
<td><strong>🏛️ Decisões de Arquitetura (ADRs)</strong></td>
|
|
311
|
-
<td>Histórico imutável de decisões técnicas, trade-offs e racional econômico.</td>
|
|
312
|
-
<td>ADR-001 em diante</td>
|
|
313
|
-
</tr>
|
|
314
|
-
<tr>
|
|
315
|
-
<td><strong>📐 Especificações Técnicas (SPECs)</strong></td>
|
|
316
|
-
<td>Contratos públicos de API, modelos de dados, endpoints e DTOs.</td>
|
|
317
|
-
<td>SPEC-001 em diante</td>
|
|
318
|
-
</tr>
|
|
319
|
-
<tr>
|
|
320
|
-
<td><strong>🛡️ Governança & Padrões Multiagente</strong></td>
|
|
321
|
-
<td>Guias operacionais locais, esteiras CI/CD, Docker e Regras de Engenharia.</td>
|
|
322
|
-
<td>GOV-001 em diante</td>
|
|
323
|
-
</tr>
|
|
324
|
-
</tbody>
|
|
325
|
-
</table>
|
|
326
|
-
<hr />
|
|
327
|
-
<h3>🚀 Topologia Técnica do Projeto (${projectContext.mode})</h3>
|
|
328
|
-
<p><strong>Stack Tecnológica Identificada:</strong></p>
|
|
329
|
-
<ul>${stackHtml}</ul>
|
|
330
|
-
<p><strong>Módulos e Componentes Arquiteturais:</strong></p>
|
|
331
|
-
<table>
|
|
332
|
-
<thead>
|
|
333
|
-
<tr>
|
|
334
|
-
<th>Módulo</th>
|
|
335
|
-
<th>Tipo</th>
|
|
336
|
-
<th>Gerenciador de Build</th>
|
|
337
|
-
</tr>
|
|
338
|
-
</thead>
|
|
339
|
-
<tbody>
|
|
340
|
-
${modulesHtml}
|
|
341
|
-
</tbody>
|
|
342
|
-
</table>
|
|
343
|
-
<hr />
|
|
344
|
-
<p><em>Página gerada e mantida autonomamente pelo Agent Master DevSecOps em conformidade com a Regra 16.</em></p>
|
|
345
|
-
`;
|
|
346
|
-
|
|
347
|
-
const updatePayload = {
|
|
348
|
-
version: { number: nextVersion },
|
|
349
|
-
title: `${projectContext.title} — Portal de Engenharia`,
|
|
350
|
-
type: 'page',
|
|
351
|
-
body: {
|
|
352
|
-
storage: {
|
|
353
|
-
value: html,
|
|
354
|
-
representation: 'storage'
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
const putRes = await requestAtlassian(creds, 'PUT', `/wiki/rest/api/content/${homepageId}`, updatePayload);
|
|
360
|
-
return {
|
|
361
|
-
status: putRes.status === 200 ? 'SUCCESS' : 'FAILED',
|
|
362
|
-
httpStatus: putRes.status,
|
|
363
|
-
homepageId,
|
|
364
|
-
version: nextVersion
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* 6. Orquestrador do Onboarding Inteligente
|
|
370
|
-
*/
|
|
371
|
-
async function runSmartOnboarding(targetDir = process.cwd(), options = {}) {
|
|
372
|
-
const projectContext = inspectProjectContext(targetDir);
|
|
373
|
-
const creds = getAtlassianCredentials(targetDir);
|
|
374
|
-
|
|
375
|
-
const defaultKey = (process.env.JIRA_PROJECT_KEY || process.env.CONFLUENCE_SPACE_KEY || projectContext.name.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6) || 'PROJ');
|
|
376
|
-
|
|
377
|
-
const report = {
|
|
378
|
-
targetDir,
|
|
379
|
-
mode: projectContext.mode,
|
|
380
|
-
projectContext,
|
|
381
|
-
credentialsConfigured: creds.isConfigured,
|
|
382
|
-
jira: { key: defaultKey, status: 'PENDING' },
|
|
383
|
-
confluence: { key: defaultKey, status: 'PENDING' },
|
|
384
|
-
homepage: { status: 'PENDING' }
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
if (!creds.isConfigured) {
|
|
388
|
-
report.jira.status = 'SKIPPED_NO_CREDENTIALS';
|
|
389
|
-
report.confluence.status = 'SKIPPED_NO_CREDENTIALS';
|
|
390
|
-
report.homepage.status = 'SKIPPED_NO_CREDENTIALS';
|
|
391
|
-
return report;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Auto-provisiona Jira
|
|
395
|
-
const jiraRes = await ensureJiraProject(creds, defaultKey, `${projectContext.title} DevSecOps`);
|
|
396
|
-
report.jira = { ...jiraRes, key: defaultKey };
|
|
397
|
-
|
|
398
|
-
// Auto-provisiona Confluence
|
|
399
|
-
const confRes = await ensureConfluenceSpace(creds, defaultKey, `${projectContext.title} DevSecOps`);
|
|
400
|
-
report.confluence = { ...confRes, key: defaultKey };
|
|
401
|
-
|
|
402
|
-
// Popula Homepage (Zero Páginas Vazias)
|
|
403
|
-
if (confRes.homepageId) {
|
|
404
|
-
const hpRes = await publishExecutiveHomepage(creds, defaultKey, confRes.homepageId, projectContext);
|
|
405
|
-
report.homepage = hpRes;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return report;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* 7. Gate Compulsório da Regra 16 (Bloqueio de Desenvolvimento de Software)
|
|
414
|
-
* Veta comandos de desenvolvimento enquanto a Tríade (Git + Jira + Confluence)
|
|
415
|
-
* não estiver 100% configurada, validada e com o portal executivo publicado.
|
|
416
|
-
*/
|
|
417
|
-
function verifyTriadGateSync(targetDir = process.cwd(), options = {}) {
|
|
418
|
-
const { checkGithubAuth } = require('./atlassian_client.js');
|
|
419
|
-
const gh = checkGithubAuth();
|
|
420
|
-
const creds = getAtlassianCredentials(targetDir);
|
|
421
|
-
const projectContext = inspectProjectContext(targetDir);
|
|
422
|
-
|
|
423
|
-
const blockers = [];
|
|
424
|
-
const defaultKey = process.env.JIRA_PROJECT_KEY || process.env.CONFLUENCE_SPACE_KEY || 'PROJ';
|
|
425
|
-
|
|
426
|
-
const status = {
|
|
427
|
-
git: {
|
|
428
|
-
ready: Boolean(gh.isAuthenticated),
|
|
429
|
-
user: gh.user || null,
|
|
430
|
-
details: gh.details || ''
|
|
431
|
-
},
|
|
432
|
-
jira: {
|
|
433
|
-
configured: Boolean(creds.domain && creds.email && creds.token),
|
|
434
|
-
key: defaultKey,
|
|
435
|
-
projectExists: Boolean(options.mockJiraExists)
|
|
436
|
-
},
|
|
437
|
-
confluence: {
|
|
438
|
-
configured: Boolean(creds.domain && creds.email && creds.token),
|
|
439
|
-
key: defaultKey,
|
|
440
|
-
spaceExists: Boolean(options.mockConfluenceExists),
|
|
441
|
-
homepageReady: Boolean(options.mockHomepageReady)
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
const isGhAuth = options.mockGithubAuthenticated !== undefined ? Boolean(options.mockGithubAuthenticated) : Boolean(gh.isAuthenticated);
|
|
446
|
-
const isAtlassianConfig = options.mockAtlassianConfigured !== undefined ? Boolean(options.mockAtlassianConfigured) : Boolean(creds.isConfigured);
|
|
447
|
-
|
|
448
|
-
status.git.ready = isGhAuth;
|
|
449
|
-
status.jira.configured = isAtlassianConfig;
|
|
450
|
-
status.confluence.configured = isAtlassianConfig;
|
|
451
|
-
|
|
452
|
-
// 1. Checa Git / GitHub CLI
|
|
453
|
-
if (!isGhAuth) {
|
|
454
|
-
blockers.push('GitHub CLI não autenticado. Execute "gh auth login" no terminal.');
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// 2. Checa Credenciais Atlassian
|
|
458
|
-
if (!isAtlassianConfig) {
|
|
459
|
-
blockers.push('Credenciais Atlassian (ATLASSIAN_DOMAIN, ATLASSIAN_EMAIL, ATLASSIAN_API_TOKEN) não configuradas no .env.');
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const canDevelop = options.forcePermit ? true : (blockers.length === 0);
|
|
463
|
-
const verdict = canDevelop ? 'LIBERADO' : 'BLOQUEADO_REGRA_16';
|
|
464
|
-
|
|
465
|
-
let gateMessage = '';
|
|
466
|
-
if (!canDevelop) {
|
|
467
|
-
gateMessage = `⛔ [GATE DE GOVERNANÇA BLOQUEADO — REGRA 16]\n` +
|
|
468
|
-
`O desenvolvimento de software (criação de código, endpoints ou refatoração) está ESTRITAMENTE BLOQUEADO.\n` +
|
|
469
|
-
`O Roteiro Inicial de certificação da Tríade (Git + Jira + Confluence) ainda não foi concluído:\n` +
|
|
470
|
-
blockers.map(b => `- ${b}`).join('\n') +
|
|
471
|
-
`\n\n👉 Ação Prescrita Obrigatória: Execute "node agent-master-devsecops/scripts/bootstrap_cli.js" ou configure seu .env e autentique a Tríade antes de iniciar o desenvolvimento.`;
|
|
472
|
-
} else {
|
|
473
|
-
gateMessage = `✅ [GATE DE GOVERNANÇA LIBERADO — REGRA 16]\n` +
|
|
474
|
-
`Tríade (Git + Jira + Confluence) 100% verificada, autêntica e operando sem falhas. Desenvolvimento de software autorizado!`;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
return {
|
|
478
|
-
canDevelop,
|
|
479
|
-
verdict,
|
|
480
|
-
gateMessage,
|
|
481
|
-
blockers,
|
|
482
|
-
status,
|
|
483
|
-
projectContext
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async function verifyTriadGate(targetDir = process.cwd(), options = {}) {
|
|
488
|
-
const { checkGithubAuth } = require('./atlassian_client.js');
|
|
489
|
-
const gh = checkGithubAuth();
|
|
490
|
-
const creds = getAtlassianCredentials(targetDir);
|
|
491
|
-
const projectContext = inspectProjectContext(targetDir);
|
|
492
|
-
|
|
493
|
-
const blockers = [];
|
|
494
|
-
const defaultKey = process.env.JIRA_PROJECT_KEY || process.env.CONFLUENCE_SPACE_KEY || 'PROJ';
|
|
495
|
-
|
|
496
|
-
const status = {
|
|
497
|
-
git: {
|
|
498
|
-
ready: Boolean(gh.isAuthenticated),
|
|
499
|
-
user: gh.user || null,
|
|
500
|
-
details: gh.details || ''
|
|
501
|
-
},
|
|
502
|
-
jira: {
|
|
503
|
-
configured: Boolean(creds.domain && creds.email && creds.token),
|
|
504
|
-
key: defaultKey,
|
|
505
|
-
projectExists: false
|
|
506
|
-
},
|
|
507
|
-
confluence: {
|
|
508
|
-
configured: Boolean(creds.domain && creds.email && creds.token),
|
|
509
|
-
key: defaultKey,
|
|
510
|
-
spaceExists: false,
|
|
511
|
-
homepageReady: false
|
|
512
|
-
}
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
const isGhAuth = options.mockGithubAuthenticated !== undefined ? Boolean(options.mockGithubAuthenticated) : Boolean(gh.isAuthenticated);
|
|
516
|
-
const isAtlassianConfig = options.mockAtlassianConfigured !== undefined ? Boolean(options.mockAtlassianConfigured) : Boolean(creds.isConfigured);
|
|
517
|
-
|
|
518
|
-
status.git.ready = isGhAuth;
|
|
519
|
-
status.jira.configured = isAtlassianConfig;
|
|
520
|
-
status.confluence.configured = isAtlassianConfig;
|
|
521
|
-
|
|
522
|
-
// 1. Checa Git / GitHub CLI
|
|
523
|
-
if (!isGhAuth) {
|
|
524
|
-
blockers.push('GitHub CLI não autenticado. Execute "gh auth login" no terminal.');
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// 2. Checa Credenciais Atlassian
|
|
528
|
-
if (!isAtlassianConfig) {
|
|
529
|
-
blockers.push('Credenciais Atlassian (ATLASSIAN_DOMAIN, ATLASSIAN_EMAIL, ATLASSIAN_API_TOKEN) não configuradas no .env.');
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// 3. Se credenciais estiverem ok, valida Jira e Confluence via API
|
|
533
|
-
if (isAtlassianConfig && !options.skipNetwork) {
|
|
534
|
-
try {
|
|
535
|
-
const jiraRes = await requestAtlassian(creds, 'GET', `/rest/api/3/project/${defaultKey}`);
|
|
536
|
-
status.jira.projectExists = (jiraRes.status === 200);
|
|
537
|
-
if (!status.jira.projectExists) {
|
|
538
|
-
blockers.push(`Projeto Jira "${defaultKey}" não encontrado na nuvem.`);
|
|
539
|
-
}
|
|
540
|
-
} catch (_) {
|
|
541
|
-
blockers.push('Falha na comunicação de rede com o Jira Cloud.');
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
try {
|
|
545
|
-
const confRes = await requestAtlassian(creds, 'GET', `/wiki/rest/api/space/${defaultKey}`);
|
|
546
|
-
status.confluence.spaceExists = (confRes.status === 200);
|
|
547
|
-
if (!status.confluence.spaceExists) {
|
|
548
|
-
blockers.push(`Espaço Confluence "${defaultKey}" não encontrado na nuvem.`);
|
|
549
|
-
} else {
|
|
550
|
-
const hpId = confRes.body?.homepage?.id || confRes.body?.homepageId;
|
|
551
|
-
status.confluence.homepageReady = Boolean(hpId);
|
|
552
|
-
}
|
|
553
|
-
} catch (_) {
|
|
554
|
-
blockers.push('Falha na comunicação de rede com o Confluence Cloud.');
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
const canDevelop = options.forcePermit ? true : (blockers.length === 0);
|
|
559
|
-
const verdict = canDevelop ? 'LIBERADO' : 'BLOQUEADO_REGRA_16';
|
|
560
|
-
|
|
561
|
-
let gateMessage = '';
|
|
562
|
-
if (!canDevelop) {
|
|
563
|
-
gateMessage = `⛔ [GATE DE GOVERNANÇA BLOQUEADO — REGRA 16]\n` +
|
|
564
|
-
`O desenvolvimento de software (criação de código, endpoints ou refatoração) está ESTRITAMENTE BLOQUEADO.\n` +
|
|
565
|
-
`O Roteiro Inicial de certificação da Tríade (Git + Jira + Confluence) ainda não foi concluído:\n` +
|
|
566
|
-
blockers.map(b => `- ${b}`).join('\n') +
|
|
567
|
-
`\n\n👉 Ação Prescrita Obrigatória: Execute "node agent-master-devsecops/scripts/bootstrap_cli.js" ou configure seu .env e autentique a Tríade antes de iniciar o desenvolvimento.`;
|
|
568
|
-
} else {
|
|
569
|
-
gateMessage = `✅ [GATE DE GOVERNANÇA LIBERADO — REGRA 16]\n` +
|
|
570
|
-
`Tríade (Git + Jira + Confluence) 100% verificada, autêntica e operando sem falhas. Desenvolvimento de software autorizado!`;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
return {
|
|
574
|
-
canDevelop,
|
|
575
|
-
verdict,
|
|
576
|
-
gateMessage,
|
|
577
|
-
blockers,
|
|
578
|
-
status,
|
|
579
|
-
projectContext
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
if (require.main === module) {
|
|
584
|
-
console.log('\n================================================================');
|
|
585
|
-
console.log('⚡ AGENT MASTER DEVSECOPS — ONBOARDING BIMODAL & AUTO-PROVISIONING');
|
|
586
|
-
console.log('================================================================\n');
|
|
587
|
-
|
|
588
|
-
runSmartOnboarding().then(report => {
|
|
589
|
-
console.log(`📂 Modo Detectado: ${report.mode}`);
|
|
590
|
-
console.log(`📦 Projeto: ${report.projectContext.title}`);
|
|
591
|
-
console.log(`🛠️ Stack: ${report.projectContext.stack.join(', ')}`);
|
|
592
|
-
console.log(`🎯 Jira: [${report.jira.key}] -> ${report.jira.status}`);
|
|
593
|
-
console.log(`📚 Confluence: [${report.confluence.key}] -> ${report.confluence.status}`);
|
|
594
|
-
console.log(`📄 Homepage: ${report.homepage.status} (Zero páginas vazias)\n`);
|
|
595
|
-
}).catch(err => {
|
|
596
|
-
console.error('Erro no onboarding:', err);
|
|
597
|
-
process.exit(1);
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
module.exports = {
|
|
602
|
-
verifyTriadGate,
|
|
603
|
-
verifyTriadGateSync,
|
|
604
|
-
detectProjectMode,
|
|
605
|
-
inspectProjectContext,
|
|
606
|
-
ensureJiraProject,
|
|
607
|
-
ensureConfluenceSpace,
|
|
608
|
-
publishExecutiveHomepage,
|
|
609
|
-
runSmartOnboarding
|
|
610
|
-
};
|