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.
Files changed (82) hide show
  1. package/README.md +205 -371
  2. package/agents/AGENT_DEVSECOPS.md +32 -417
  3. package/agents/AGENT_INFRA.md +31 -0
  4. package/agents/AGENT_ORCHESTRATOR.md +39 -264
  5. package/agents/AGENT_QA.md +58 -186
  6. package/agents/AGENT_SCRUM.md +23 -190
  7. package/agents/AGENT_SRE.md +22 -173
  8. package/agents/AGENT_UIUX.md +26 -162
  9. package/docs/ARCHITECTURE.md +152 -148
  10. package/docs/CALCULATIONS.md +17 -17
  11. package/docs/CHANGELOG.md +112 -49
  12. package/docs/ENGINEERING_RULES.md +56 -252
  13. package/docs/IDEAS.md +17 -17
  14. package/docs/LOCAL_BACKLOG.md +20 -0
  15. package/docs/STATE.md +111 -43
  16. package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -58
  17. package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -58
  18. package/package.json +59 -47
  19. package/scripts/auditors/a11y_auditor.js +462 -0
  20. package/scripts/auditors/code_quality_auditor.js +444 -0
  21. package/scripts/auditors/contract_test_validator.js +396 -0
  22. package/scripts/auditors/eval_harness.js +213 -0
  23. package/scripts/auditors/git_hygiene_linter.js +391 -0
  24. package/scripts/auditors/pr_reviewer.js +627 -0
  25. package/scripts/auditors/sast_scanner.js +442 -0
  26. package/scripts/auditors/sca_dependency_auditor.js +673 -0
  27. package/scripts/{bootstrap_cli.js → bin/bootstrap_cli.js} +597 -468
  28. package/scripts/bin/domain_idea_miner.js +658 -0
  29. package/scripts/bin/smart_onboarding.js +905 -0
  30. package/scripts/{web_inspector.js → bin/web_inspector.js} +578 -355
  31. package/scripts/core/architecture_advisor.js +378 -0
  32. package/scripts/core/ast_skeletonizer.js +413 -0
  33. package/scripts/core/autonomous_remediation_engine.js +305 -0
  34. package/scripts/{circuit_breaker.js → core/circuit_breaker.js} +546 -330
  35. package/scripts/core/context_compressor.js +237 -0
  36. package/scripts/core/fast_feature_scaffold.js +840 -0
  37. package/scripts/core/hot_state_cache.js +238 -0
  38. package/scripts/core/knowledge_gateway.js +888 -0
  39. package/scripts/{magic_prompt_generator.js → core/magic_prompt_generator.js} +250 -205
  40. package/scripts/{multi_agent_orchestrator.js → core/multi_agent_orchestrator.js} +421 -258
  41. package/scripts/{otel_tracer.js → core/otel_tracer.js} +335 -228
  42. package/scripts/{perf_benchmark.js → core/perf_benchmark.js} +212 -153
  43. package/scripts/core/sarif_engine.js +299 -0
  44. package/scripts/{sre_remediation_engine.js → core/sre_remediation_engine.js} +236 -174
  45. package/scripts/{universal_packager.js → core/universal_packager.js} +357 -313
  46. package/scripts/integrations/atlassian_client.js +600 -0
  47. package/scripts/integrations/atlassian_sync_action.js +576 -0
  48. package/scripts/integrations/confluence_sync.js +684 -0
  49. package/scripts/integrations/credentials_guard.js +500 -0
  50. package/scripts/integrations/jira_manager.js +722 -0
  51. package/scripts/mcp/handlers/atlassian_handlers.js +300 -0
  52. package/scripts/mcp/handlers/core_handlers.js +419 -0
  53. package/scripts/mcp/handlers/qa_handlers.js +502 -0
  54. package/scripts/mcp/handlers/rules_handlers.js +379 -0
  55. package/scripts/mcp/handlers/scaffold_handlers.js +334 -0
  56. package/scripts/mcp/handlers/sre_handlers.js +443 -0
  57. package/scripts/mcp/resources_registry.js +453 -0
  58. package/scripts/mcp/server.js +408 -0
  59. package/scripts/mcp/tool_handlers.js +444 -0
  60. package/scripts/mcp/tools_registry.js +1123 -0
  61. package/scripts/mcp_server.js +236 -1884
  62. package/.cursorignore +0 -23
  63. package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +0 -53
  64. package/scripts/a11y_auditor.js +0 -187
  65. package/scripts/ast_skeletonizer.js +0 -220
  66. package/scripts/atlassian_client.js +0 -238
  67. package/scripts/atlassian_sync_action.js +0 -269
  68. package/scripts/code_quality_auditor.js +0 -142
  69. package/scripts/confluence_sync.js +0 -337
  70. package/scripts/context_compressor.js +0 -139
  71. package/scripts/contract_test_validator.js +0 -166
  72. package/scripts/credentials_guard.js +0 -216
  73. package/scripts/domain_idea_miner.js +0 -364
  74. package/scripts/eval_harness.js +0 -100
  75. package/scripts/fast_feature_scaffold.js +0 -338
  76. package/scripts/git_hygiene_linter.js +0 -186
  77. package/scripts/hot_state_cache.js +0 -95
  78. package/scripts/jira_manager.js +0 -408
  79. package/scripts/pr_reviewer.js +0 -373
  80. package/scripts/sast_scanner.js +0 -180
  81. package/scripts/sca_dependency_auditor.js +0 -167
  82. package/scripts/smart_onboarding.js +0 -610
@@ -0,0 +1,576 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/integrations/atlassian_sync_action.js
4
+ *
5
+ * Sincronizador Automático Git ↔ Atlassian via Actions & Local (AGDEV-23 / AGDEV-35).
6
+ * Conecta automaticamente commits, branches, Pull Requests e páginas do Confluence
7
+ * aos cards do Jira utilizando a API oficial de Remote Links do Jira Cloud (REST API v3).
8
+ *
9
+ * Governança: Tríade Git + Jira + Confluence (Regra 11 e Regra 15).
10
+ * Conformidade: OASIS SARIF v2.1.0, Programação Defensiva e Clean Code.
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const { execFileSync } = require('node:child_process');
16
+ const {
17
+ getAtlassianCredentials,
18
+ getGitMetadata,
19
+ getGitRepositoryUrl,
20
+ requestAtlassian,
21
+ AtlassianClient
22
+ } = require('./atlassian_client.js');
23
+
24
+ const { validateRule15 } = require('./credentials_guard.js');
25
+ const { createSarifReport } = require('../core/sarif_engine.js');
26
+
27
+ /**
28
+ * Obtém a credencial em base64 da Atlassian para compatibilidade retroativa.
29
+ * @returns {string|null}
30
+ */
31
+ function getAuth() {
32
+ const cfg = getAtlassianCredentials();
33
+ return cfg.authBase64;
34
+ }
35
+
36
+ /**
37
+ * Extrai chaves de tickets Jira (ex: AGDEV-123, PROJ-456) a partir de um texto.
38
+ * Case-insensitive na leitura com normalização em maiúsculas e deduplicação.
39
+ * @param {string} text
40
+ * @returns {Array<string>}
41
+ */
42
+ function extractIssueKeys(text) {
43
+ if (!text || typeof text !== 'string') return [];
44
+ const matches = text.match(/\b[A-Za-z]{2,10}-\d+\b/g) || [];
45
+ return [...new Set(matches.map(k => k.toUpperCase()))];
46
+ }
47
+
48
+ /**
49
+ * Extrai o identificador numérico de um Pull Request (#123, pull request #123 ou URL /pull/123).
50
+ * @param {string} text
51
+ * @returns {string|null}
52
+ */
53
+ function extractPrNumber(text) {
54
+ if (!text || typeof text !== 'string') return null;
55
+ const match = text.match(/#(\d+)\b/) ||
56
+ text.match(/pull request #(\d+)/i) ||
57
+ text.match(/\/pull\/(\d+)\b/i);
58
+ return match ? match[1] : null;
59
+ }
60
+
61
+ /**
62
+ * Formata o payload canônico de Remote Link para a API v3 do Jira Cloud.
63
+ * @param {string} url
64
+ * @param {string} title
65
+ * @param {string} [type='commit']
66
+ * @param {string|null} [globalId=null]
67
+ * @param {string|null} [summary=null]
68
+ * @returns {object}
69
+ */
70
+ function formatRemoteLinkPayload(url, title, type = 'commit', globalId = null, summary = null) {
71
+ const safeType = (typeof type === 'string') ? type.toLowerCase() : 'commit';
72
+ let iconUrl = 'https://github.githubassets.com/favicons/favicon.png';
73
+ let iconTitle = `GitHub ${safeType.toUpperCase()}`;
74
+ let relationship = 'causes';
75
+
76
+ if (safeType === 'pr') {
77
+ relationship = 'relates to';
78
+ } else if (safeType === 'branch') {
79
+ relationship = 'builds';
80
+ } else if (safeType === 'confluence' || safeType === 'wiki') {
81
+ relationship = undefined;
82
+ iconUrl = 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/confluence.png';
83
+ iconTitle = 'Confluence Page';
84
+ }
85
+
86
+ const payload = {
87
+ object: {
88
+ url: typeof url === 'string' ? url : '',
89
+ title: typeof title === 'string' ? title : '',
90
+ icon: {
91
+ url16x16: iconUrl,
92
+ title: iconTitle
93
+ }
94
+ }
95
+ };
96
+
97
+ if (relationship) {
98
+ payload.relationship = relationship;
99
+ }
100
+ if (globalId) {
101
+ payload.globalId = globalId;
102
+ }
103
+ if (summary) {
104
+ payload.object.summary = summary;
105
+ }
106
+
107
+ return payload;
108
+ }
109
+
110
+ /**
111
+ * Vincula uma página do Confluence como Remote Link em uma issue do Jira.
112
+ * @param {string} issueKey
113
+ * @param {string} pageUrl
114
+ * @param {string} pageTitle
115
+ * @param {string|null} [summary=null]
116
+ * @returns {Promise<object>}
117
+ */
118
+ async function linkConfluencePageToJira(issueKey, pageUrl, pageTitle, summary = null) {
119
+ const atlassian = getAtlassianCredentials();
120
+ const globalId = `system=confluence&pageUrl=${encodeURIComponent(pageUrl)}`;
121
+ return await linkUrlToJira(
122
+ issueKey,
123
+ pageUrl,
124
+ pageTitle,
125
+ 'confluence',
126
+ atlassian.authBase64,
127
+ globalId,
128
+ summary
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Registra um Remote Link genérico (commit, branch, PR ou doc) em uma issue do Jira.
134
+ * @param {string} issueKey
135
+ * @param {string} url
136
+ * @param {string} title
137
+ * @param {string} [type='commit']
138
+ * @param {string|null} [authOverride=null]
139
+ * @param {string|null} [globalId=null]
140
+ * @param {string|null} [summary=null]
141
+ * @returns {Promise<object>}
142
+ */
143
+ async function linkUrlToJira(issueKey, url, title, type = 'commit', authOverride = null, globalId = null, summary = null) {
144
+ const atlassian = getAtlassianCredentials();
145
+ const auth = (authOverride !== null && authOverride !== undefined) ? authOverride : atlassian.authBase64;
146
+ if (!auth) {
147
+ return { status: 'FAILED', message: 'Credenciais Atlassian não configuradas no ambiente (Regra 15).' };
148
+ }
149
+
150
+ const cleanDomain = atlassian.domain;
151
+ if (!cleanDomain) {
152
+ return { status: 'FAILED', message: 'Domínio Atlassian não configurado para este projeto (Regra 15).' };
153
+ }
154
+
155
+ const payload = formatRemoteLinkPayload(url, title, type, globalId, summary);
156
+ const endpoint = `/rest/api/3/issue/${encodeURIComponent(issueKey)}/remotelink`;
157
+
158
+ try {
159
+ const creds = {
160
+ domain: cleanDomain,
161
+ auth,
162
+ authBase64: auth
163
+ };
164
+ const res = await requestAtlassian(creds, 'POST', endpoint, payload);
165
+
166
+ if (res.status === 200 || res.status === 201) {
167
+ return { status: 'SUCCESS', issueKey, url, type, httpStatus: res.status };
168
+ } else {
169
+ const errText = typeof res.body === 'object' ? JSON.stringify(res.body) : String(res.body || '');
170
+ return { status: 'FAILED', issueKey, error: `HTTP ${res.status}: ${errText}` };
171
+ }
172
+ } catch (err) {
173
+ return { status: 'ERROR', issueKey, error: err.message };
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Sincroniza o contexto atual de Git (commit HEAD, branch atual e PR se detectado).
179
+ * @param {string} [targetDir=process.cwd()]
180
+ * @returns {Promise<object>}
181
+ */
182
+ async function syncCurrentContext(targetDir = process.cwd()) {
183
+ const atlassian = getAtlassianCredentials(targetDir);
184
+ const gitMeta = getGitMetadata();
185
+ const repoUrl = gitMeta.repoUrl || 'https://github.com/ContalApps/agent-master-devsecops';
186
+ const repoSlug = repoUrl.replace('https://github.com/', '');
187
+
188
+ const sha = gitMeta.sha;
189
+ const branch = gitMeta.branch;
190
+ const message = gitMeta.message;
191
+ const prNumber = process.env.PR_NUMBER || extractPrNumber(process.env.PR_TITLE || '') || extractPrNumber(message);
192
+
193
+ const combined = `${branch} ${message} ${process.env.PR_TITLE || ''}`;
194
+ const keys = extractIssueKeys(combined);
195
+
196
+ if (keys.length === 0) {
197
+ return {
198
+ synced: false,
199
+ keys: [],
200
+ reason: 'Nenhuma chave Jira encontrada na branch ou mensagem de commit.',
201
+ results: []
202
+ };
203
+ }
204
+
205
+ const results = [];
206
+
207
+ for (const key of keys) {
208
+ if (sha && repoUrl) {
209
+ const commitGlobalId = `system=github&repo=${repoSlug}&type=commit&sha=${sha}`;
210
+ const commitRes = await linkUrlToJira(
211
+ key,
212
+ `${repoUrl}/commit/${sha}`,
213
+ `GitHub Commit: ${sha.slice(0, 7)} (${message})`,
214
+ 'commit',
215
+ atlassian.authBase64,
216
+ commitGlobalId
217
+ );
218
+ results.push({ key, type: 'commit', ref: sha.slice(0, 7), status: commitRes.status, detail: commitRes });
219
+ }
220
+
221
+ if (branch && branch !== 'main' && branch !== 'HEAD' && repoUrl) {
222
+ const branchGlobalId = `system=github&repo=${repoSlug}&type=branch&name=${branch}`;
223
+ const branchRes = await linkUrlToJira(
224
+ key,
225
+ `${repoUrl}/tree/${branch}`,
226
+ `GitHub Branch: ${branch}`,
227
+ 'branch',
228
+ atlassian.authBase64,
229
+ branchGlobalId
230
+ );
231
+ results.push({ key, type: 'branch', ref: branch, status: branchRes.status, detail: branchRes });
232
+ }
233
+
234
+ if (prNumber && repoUrl) {
235
+ const prGlobalId = `system=github&repo=${repoSlug}&type=pr&id=${prNumber}`;
236
+ const prRes = await linkUrlToJira(
237
+ key,
238
+ `${repoUrl}/pull/${prNumber}`,
239
+ `GitHub Pull Request #${prNumber}`,
240
+ 'pr',
241
+ atlassian.authBase64,
242
+ prGlobalId,
243
+ process.env.PR_TITLE || message
244
+ );
245
+ results.push({ key, type: 'pr', ref: `#${prNumber}`, status: prRes.status, detail: prRes });
246
+ }
247
+ }
248
+
249
+ return {
250
+ synced: true,
251
+ keys,
252
+ results
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Executa a sincronização retroativa (backfill) de commits recentes.
258
+ * @param {number} [limit=30]
259
+ * @returns {Promise<Array<object>>}
260
+ */
261
+ async function backfillHistory(limit = 30) {
262
+ console.log(`[Atlassian Sync] Iniciando sincronização retroativa dos últimos ${limit} commits...`);
263
+ const atlassian = getAtlassianCredentials();
264
+ if (!atlassian.isConfigured) {
265
+ throw new Error('Credenciais Atlassian ausentes para sincronização retroativa.');
266
+ }
267
+
268
+ const repoUrl = getGitRepositoryUrl();
269
+ const repoSlug = repoUrl.replace('https://github.com/', '');
270
+
271
+ let rawLog = '';
272
+ try {
273
+ rawLog = execFileSync('git', ['log', `-${limit}`, '--format=%H|%s'], { encoding: 'utf8' });
274
+ } catch (err) {
275
+ console.error('[Atlassian Sync] Falha ao extrair histórico git:', err.message);
276
+ return [];
277
+ }
278
+
279
+ const lines = rawLog.split('\n').filter(l => l.trim().length > 0);
280
+ const results = [];
281
+
282
+ for (const line of lines) {
283
+ const [sha, ...subjectParts] = line.split('|');
284
+ const subject = subjectParts.join('|');
285
+ const keys = extractIssueKeys(subject);
286
+ const prNumber = extractPrNumber(subject);
287
+
288
+ for (const key of keys) {
289
+ // 1. Link Commit
290
+ const commitGlobalId = `system=github&repo=${repoSlug}&type=commit&sha=${sha}`;
291
+ const commitRes = await linkUrlToJira(
292
+ key,
293
+ `${repoUrl}/commit/${sha}`,
294
+ `GitHub Commit: ${sha.slice(0, 7)} (${subject})`,
295
+ 'commit',
296
+ atlassian.authBase64,
297
+ commitGlobalId
298
+ );
299
+ results.push({ key, type: 'commit', ref: sha.slice(0, 7), status: commitRes.status });
300
+
301
+ // 2. Link PR se presente no commit title
302
+ if (prNumber) {
303
+ const prGlobalId = `system=github&repo=${repoSlug}&type=pr&id=${prNumber}`;
304
+ const prRes = await linkUrlToJira(
305
+ key,
306
+ `${repoUrl}/pull/${prNumber}`,
307
+ `GitHub Pull Request #${prNumber}`,
308
+ 'pr',
309
+ atlassian.authBase64,
310
+ prGlobalId,
311
+ subject
312
+ );
313
+ results.push({ key, type: 'pr', ref: `#${prNumber}`, status: prRes.status });
314
+ }
315
+ }
316
+ }
317
+
318
+ const successCount = results.filter(r => r.status === 'SUCCESS').length;
319
+ console.log(`[Atlassian Sync] Sincronização retroativa concluída: ${successCount}/${results.length} links processados com sucesso.`);
320
+ return results;
321
+ }
322
+
323
+ /**
324
+ * Formata um relatório executivo em Markdown com o resultado da sincronização de links.
325
+ * @param {object} [syncSummary={}]
326
+ * @returns {string}
327
+ */
328
+ function formatReport(syncSummary = {}) {
329
+ let md = '# 🔗 Atlassian Sync Action — Remote Links Status Report\n\n';
330
+
331
+ const keys = syncSummary.keys || [];
332
+ const results = syncSummary.results || [];
333
+
334
+ md += `**Chaves Jira Vinculadas:** \`${keys.length > 0 ? keys.join(', ') : 'NENHUMA'}\`\n`;
335
+ md += `**Total de Links Processados:** \`${results.length}\`\n\n`;
336
+
337
+ if (results.length === 0) {
338
+ md += syncSummary.reason ? `*Observação:* ${syncSummary.reason}\n` : '*Nenhum link para sincronizar no contexto atual.*\n';
339
+ return md;
340
+ }
341
+
342
+ md += '| Chave Jira | Tipo | Referência | Status |\n';
343
+ md += '| :--- | :--- | :--- | :--- |\n';
344
+ for (const r of results) {
345
+ const icon = r.status === 'SUCCESS' ? '✅' : '❌';
346
+ md += `| \`${r.key}\` | \`${r.type}\` | \`${r.ref}\` | ${icon} \`${r.status}\` |\n`;
347
+ }
348
+
349
+ return md;
350
+ }
351
+
352
+ /**
353
+ * Audita o resultado da sincronização de links emitindo relatório OASIS SARIF v2.1.0.
354
+ * @param {object} [syncSummary={}]
355
+ * @returns {object} SARIF v2.1.0 JSON object
356
+ */
357
+ function auditSyncToSarif(syncSummary = {}) {
358
+ const rules = [
359
+ {
360
+ id: 'RULE-SYNC-001',
361
+ name: 'JiraIssueKeyMissing',
362
+ severity: 'WARNING',
363
+ message: 'Nenhuma chave de issue Jira identificada no contexto atual do Git (Regra 11).',
364
+ remediation: 'Incluir a chave Jira no nome da branch ou na mensagem do commit: [<TICKET>] - <Ação em português>.',
365
+ cwe: 'CWE-1059'
366
+ },
367
+ {
368
+ id: 'RULE-SYNC-002',
369
+ name: 'RemoteLinkSyncFailed',
370
+ severity: 'ERROR',
371
+ message: 'Falha ao sincronizar Remote Link no card do Jira.',
372
+ remediation: 'Verifique credenciais Atlassian (Regra 15), permissões de projeto ou validade da chave Jira.',
373
+ cwe: 'CWE-287'
374
+ },
375
+ {
376
+ id: 'RULE-SYNC-003',
377
+ name: 'RemoteLinkSyncSuccess',
378
+ severity: 'NOTE',
379
+ message: 'Remote Link sincronizado com sucesso no card do Jira.',
380
+ remediation: 'Nenhuma ação necessária. Vínculo ativo e registrado.',
381
+ cwe: 'CWE-Unknown'
382
+ }
383
+ ];
384
+
385
+ const findings = [];
386
+ const keys = syncSummary.keys || [];
387
+ const results = syncSummary.results || [];
388
+
389
+ if (keys.length === 0) {
390
+ findings.push({
391
+ ruleId: 'RULE-SYNC-001',
392
+ severity: 'WARNING',
393
+ message: syncSummary.reason || 'Nenhuma chave Jira identificada no contexto atual.',
394
+ filename: '.git',
395
+ line: 1,
396
+ column: 1
397
+ });
398
+ }
399
+
400
+ for (const r of results) {
401
+ if (r.status !== 'SUCCESS') {
402
+ findings.push({
403
+ ruleId: 'RULE-SYNC-002',
404
+ severity: 'ERROR',
405
+ message: `Falha ao registrar remote link (${r.type}: ${r.ref}) para a chave ${r.key}.`,
406
+ filename: '.git',
407
+ line: 1,
408
+ column: 1
409
+ });
410
+ }
411
+ }
412
+
413
+ return createSarifReport({
414
+ toolName: 'agent-master-atlassian-sync-action',
415
+ toolVersion: '5.1.0',
416
+ rules,
417
+ findings,
418
+ properties: {
419
+ category: 'Governance & Remote Links Sync',
420
+ keysCount: keys.length,
421
+ linksCount: results.length
422
+ }
423
+ });
424
+ }
425
+
426
+ /**
427
+ * Classe Canônica AtlassianSyncAction.
428
+ * Fornece API orientada a objetos para orquestração da sincronização Git-Jira.
429
+ */
430
+ class AtlassianSyncAction {
431
+ constructor(options = {}) {
432
+ this.options = Object.freeze({
433
+ targetDir: options.targetDir || process.cwd(),
434
+ timeout: options.timeout || 30000,
435
+ ...options
436
+ });
437
+ this.atlassianClient = new AtlassianClient(this.options.targetDir, { timeout: this.options.timeout });
438
+ }
439
+
440
+ getAuth() {
441
+ return this.atlassianClient.getCredentials().authBase64;
442
+ }
443
+
444
+ extractKeys(text) {
445
+ return extractIssueKeys(text);
446
+ }
447
+
448
+ extractPr(text) {
449
+ return extractPrNumber(text);
450
+ }
451
+
452
+ formatPayload(url, title, type, globalId, summary) {
453
+ return formatRemoteLinkPayload(url, title, type, globalId, summary);
454
+ }
455
+
456
+ async linkUrl(issueKey, url, title, type = 'commit', globalId = null, summary = null) {
457
+ return linkUrlToJira(issueKey, url, title, type, null, globalId, summary);
458
+ }
459
+
460
+ async linkConfluence(issueKey, pageUrl, pageTitle, summary = null) {
461
+ return linkConfluencePageToJira(issueKey, pageUrl, pageTitle, summary);
462
+ }
463
+
464
+ async backfill(limit = 30) {
465
+ return backfillHistory(limit);
466
+ }
467
+
468
+ async syncCurrentContext() {
469
+ return syncCurrentContext(this.options.targetDir);
470
+ }
471
+
472
+ formatReport(syncResults = {}) {
473
+ return formatReport(syncResults);
474
+ }
475
+
476
+ auditToSarif(syncResults = {}) {
477
+ return auditSyncToSarif(syncResults);
478
+ }
479
+
480
+ // Métodos Estáticos Canônicos
481
+ static extractIssueKeys(text) {
482
+ return extractIssueKeys(text);
483
+ }
484
+
485
+ static extractPrNumber(text) {
486
+ return extractPrNumber(text);
487
+ }
488
+
489
+ static formatRemoteLinkPayload(url, title, type, globalId, summary) {
490
+ return formatRemoteLinkPayload(url, title, type, globalId, summary);
491
+ }
492
+
493
+ static async linkUrlToJira(issueKey, url, title, type, authOverride, globalId, summary) {
494
+ return linkUrlToJira(issueKey, url, title, type, authOverride, globalId, summary);
495
+ }
496
+
497
+ static async linkConfluencePageToJira(issueKey, pageUrl, pageTitle, summary) {
498
+ return linkConfluencePageToJira(issueKey, pageUrl, pageTitle, summary);
499
+ }
500
+
501
+ static async backfillHistory(limit) {
502
+ return backfillHistory(limit);
503
+ }
504
+
505
+ static async syncCurrentContext(targetDir) {
506
+ return syncCurrentContext(targetDir);
507
+ }
508
+
509
+ static formatReport(syncResults) {
510
+ return formatReport(syncResults);
511
+ }
512
+
513
+ static auditToSarif(syncResults) {
514
+ return auditSyncToSarif(syncResults);
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Ponto de entrada CLI para execução no GitHub Actions ou terminal local.
520
+ */
521
+ async function runCli() {
522
+ const isCI = process.env.GITHUB_ACTIONS === 'true';
523
+
524
+ // Validação Compulsória de Requisitos (Regra 15)
525
+ // Em CI, a ausência de secrets é ERRO CRÍTICO para impedir perda silenciosa
526
+ const validation = validateRule15({ failFast: isCI, requireGitHub: false, requireAtlassian: true });
527
+ if (!validation.valid) {
528
+ console.error(`[Atlassian Sync] Falha de validação da Regra 15:\n ${validation.missing.join(', ')}`);
529
+ console.error(` Remediação: ${validation.remediation.join('\n ')}`);
530
+ if (isCI) {
531
+ process.exit(1);
532
+ }
533
+ return;
534
+ }
535
+
536
+ const args = process.argv.slice(2);
537
+ if (args.includes('--retroactive') || args.includes('-r')) {
538
+ const limitIdx = args.findIndex(a => a === '--retroactive' || a === '-r');
539
+ const limitVal = parseInt(args[limitIdx + 1], 10) || 30;
540
+ await backfillHistory(limitVal);
541
+ return;
542
+ }
543
+
544
+ const syncOutcome = await syncCurrentContext();
545
+ if (!syncOutcome.synced) {
546
+ console.log(`[Atlassian Sync] ${syncOutcome.reason}`);
547
+ return;
548
+ }
549
+
550
+ console.log(`[Atlassian Sync] Chaves Jira identificadas: ${syncOutcome.keys.join(', ')}`);
551
+ for (const r of syncOutcome.results) {
552
+ console.log(`[Atlassian Sync] ${r.type.toUpperCase()} ${r.ref} vinculado a ${r.key}: ${r.status}`);
553
+ }
554
+ }
555
+
556
+ if (require.main === module) {
557
+ runCli().catch(err => {
558
+ console.error('[Atlassian Sync] Erro fatal na execução:', err);
559
+ process.exit(1);
560
+ });
561
+ }
562
+
563
+ module.exports = {
564
+ AtlassianSyncAction,
565
+ getAuth,
566
+ extractIssueKeys,
567
+ extractPrNumber,
568
+ formatRemoteLinkPayload,
569
+ linkUrlToJira,
570
+ linkConfluencePageToJira,
571
+ backfillHistory,
572
+ syncCurrentContext,
573
+ formatReport,
574
+ auditSyncToSarif,
575
+ runCli
576
+ };