agent-master-devsecops 0.0.0-stage → 3.9.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 (47) hide show
  1. package/.cursorignore +23 -0
  2. package/README.md +371 -3
  3. package/agents/AGENT_DEVSECOPS.md +417 -0
  4. package/agents/AGENT_ORCHESTRATOR.md +264 -0
  5. package/agents/AGENT_QA.md +186 -0
  6. package/agents/AGENT_SCRUM.md +190 -0
  7. package/agents/AGENT_SRE.md +173 -0
  8. package/agents/AGENT_UIUX.md +162 -0
  9. package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +53 -0
  10. package/docs/ARCHITECTURE.md +148 -0
  11. package/docs/CALCULATIONS.md +17 -0
  12. package/docs/CHANGELOG.md +49 -0
  13. package/docs/ENGINEERING_RULES.md +252 -0
  14. package/docs/IDEAS.md +17 -0
  15. package/docs/STATE.md +43 -0
  16. package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -0
  17. package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -0
  18. package/package.json +47 -6
  19. package/scripts/a11y_auditor.js +187 -0
  20. package/scripts/ast_skeletonizer.js +220 -0
  21. package/scripts/atlassian_client.js +238 -0
  22. package/scripts/atlassian_sync_action.js +269 -0
  23. package/scripts/bootstrap_cli.js +468 -0
  24. package/scripts/circuit_breaker.js +330 -0
  25. package/scripts/code_quality_auditor.js +142 -0
  26. package/scripts/confluence_sync.js +337 -0
  27. package/scripts/context_compressor.js +139 -0
  28. package/scripts/contract_test_validator.js +166 -0
  29. package/scripts/credentials_guard.js +216 -0
  30. package/scripts/domain_idea_miner.js +364 -0
  31. package/scripts/eval_harness.js +100 -0
  32. package/scripts/fast_feature_scaffold.js +338 -0
  33. package/scripts/git_hygiene_linter.js +186 -0
  34. package/scripts/hot_state_cache.js +95 -0
  35. package/scripts/jira_manager.js +408 -0
  36. package/scripts/magic_prompt_generator.js +205 -0
  37. package/scripts/mcp_server.js +1884 -0
  38. package/scripts/multi_agent_orchestrator.js +258 -0
  39. package/scripts/otel_tracer.js +228 -0
  40. package/scripts/perf_benchmark.js +153 -0
  41. package/scripts/pr_reviewer.js +373 -0
  42. package/scripts/sast_scanner.js +180 -0
  43. package/scripts/sca_dependency_auditor.js +167 -0
  44. package/scripts/smart_onboarding.js +610 -0
  45. package/scripts/sre_remediation_engine.js +174 -0
  46. package/scripts/universal_packager.js +313 -0
  47. package/scripts/web_inspector.js +355 -0
@@ -0,0 +1,408 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/jira_manager.js
4
+ *
5
+ * Gerenciador Unificado do Jira: Padronização de Kanban, WIP Limits, Sprints e Time-Tracking.
6
+ * Consolida o controle de fluxo ágil dos 3 Kanbans canônicos ("A fazer", "Em andamento", "Concluído")
7
+ * com o registro automatizado de worklogs e timetracking durante o ciclo de desenvolvimento.
8
+ *
9
+ * Governança: Tríade Git + Jira + Confluence (Regra 11 e AGENT_SCRUM).
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+
17
+ const {
18
+ getAtlassianCredentials,
19
+ requestAtlassian,
20
+ getGitMetadata
21
+ } = require('./atlassian_client.js');
22
+
23
+ // ==========================================
24
+ // 1. KANBAN CANÔNICO & PADRONIZAÇÃO DE QUADRO
25
+ // ==========================================
26
+
27
+ const CANONICAL_KANBANS = Object.freeze({
28
+ TODO: 'A fazer',
29
+ IN_PROGRESS: 'Em andamento',
30
+ DONE: 'Concluído'
31
+ });
32
+
33
+ const CANONICAL_COLUMNS_LIST = [
34
+ CANONICAL_KANBANS.TODO,
35
+ CANONICAL_KANBANS.IN_PROGRESS,
36
+ CANONICAL_KANBANS.DONE
37
+ ];
38
+
39
+ /**
40
+ * Sanitiza o summary de um card do Jira, removendo chaves redundantes do título.
41
+ * Exemplo: "[PROJ-123] Deploy de Produção" -> "Deploy de Produção"
42
+ */
43
+ function sanitizeIssueSummary(summary = '') {
44
+ if (!summary) return '';
45
+ return summary.replace(/^\s*\[[A-Z]{2,10}-\d+\]\s*[:-]?\s*/i, '').trim();
46
+ }
47
+
48
+ /**
49
+ * Valida o limite de trabalho em progresso (WIP Limit).
50
+ */
51
+ function checkWipLimit(inProgressCount = 0, maxWip = 2) {
52
+ return {
53
+ inProgressCount,
54
+ maxWip,
55
+ isSaturated: inProgressCount >= maxWip,
56
+ canPullNewTask: inProgressCount < maxWip,
57
+ alertMessage: inProgressCount >= maxWip
58
+ ? `WIP SATURADO: ${inProgressCount} tarefas ativas em andamento (limite máximo: ${maxWip}). Conclua tarefas antes de puxar novas.`
59
+ : null
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Mapeia qualquer status ou nome de coluna legado do Jira para uma das 3 colunas canônicas.
65
+ */
66
+ function resolveCanonicalColumn(statusName = '', statusCategory = '') {
67
+ const normName = String(statusName).trim().toLowerCase();
68
+ const normCat = String(statusCategory).trim().toLowerCase();
69
+
70
+ // 1. Concluído / Done
71
+ if (
72
+ normName.includes('concl') ||
73
+ normName.includes('done') ||
74
+ normName.includes('finaliz') ||
75
+ normName.includes('resolv') ||
76
+ normCat.includes('conclu') ||
77
+ normCat.includes('done') ||
78
+ normCat.includes('complete')
79
+ ) {
80
+ return CANONICAL_KANBANS.DONE;
81
+ }
82
+
83
+ // 2. Em andamento / In Progress
84
+ if (
85
+ normName.includes('andamento') ||
86
+ normName.includes('progress') ||
87
+ normName.includes('desenvolv') ||
88
+ normName.includes('análise') ||
89
+ normName.includes('analise') ||
90
+ normCat.includes('andamento') ||
91
+ normCat.includes('progress') ||
92
+ normCat.includes('indeterminate')
93
+ ) {
94
+ return CANONICAL_KANBANS.IN_PROGRESS;
95
+ }
96
+
97
+ // 3. A fazer / To Do / Backlog
98
+ return CANONICAL_KANBANS.TODO;
99
+ }
100
+
101
+ /**
102
+ * Normaliza e consolida as colunas de uma configuração de Board do Jira para os 3 Kanbans padrão.
103
+ */
104
+ function standardizeBoardColumns(boardConfig = {}) {
105
+ const rawColumns = (boardConfig && boardConfig.columnConfig && boardConfig.columnConfig.columns) || [];
106
+
107
+ const standardized = {
108
+ canonicalColumns: [...CANONICAL_COLUMNS_LIST],
109
+ columns: [
110
+ { name: CANONICAL_KANBANS.TODO, statuses: [] },
111
+ { name: CANONICAL_KANBANS.IN_PROGRESS, statuses: [] },
112
+ { name: CANONICAL_KANBANS.DONE, statuses: [] }
113
+ ],
114
+ remappedCount: 0
115
+ };
116
+
117
+ const todoCol = standardized.columns[0];
118
+ const inProgCol = standardized.columns[1];
119
+ const doneCol = standardized.columns[2];
120
+
121
+ for (const col of rawColumns) {
122
+ const canonicalTarget = resolveCanonicalColumn(col.name);
123
+ const statuses = col.statuses || [];
124
+
125
+ if (canonicalTarget === CANONICAL_KANBANS.DONE) {
126
+ doneCol.statuses.push(...statuses);
127
+ } else if (canonicalTarget === CANONICAL_KANBANS.IN_PROGRESS) {
128
+ inProgCol.statuses.push(...statuses);
129
+ } else {
130
+ todoCol.statuses.push(...statuses);
131
+ }
132
+
133
+ if (col.name !== canonicalTarget) {
134
+ standardized.remappedCount++;
135
+ }
136
+ }
137
+
138
+ return standardized;
139
+ }
140
+
141
+ /**
142
+ * Constrói a consulta JQL padronizada para uma das 3 colunas canônicas no Jira.
143
+ */
144
+ function buildCanonicalJql(projectKey, canonicalColumn) {
145
+ if (!projectKey) throw new Error('projectKey é obrigatório para construir JQL.');
146
+
147
+ switch (canonicalColumn) {
148
+ case CANONICAL_KANBANS.TODO:
149
+ return `project = "${projectKey}" AND statusCategory in ("To Do", "Undefined") ORDER BY rank ASC, created ASC`;
150
+ case CANONICAL_KANBANS.IN_PROGRESS:
151
+ return `project = "${projectKey}" AND statusCategory in ("In Progress") ORDER BY rank ASC, updated DESC`;
152
+ case CANONICAL_KANBANS.DONE:
153
+ return `project = "${projectKey}" AND statusCategory in ("Done") ORDER BY updated DESC`;
154
+ default:
155
+ throw new Error(`Coluna desconhecida: "${canonicalColumn}". Use: ${CANONICAL_COLUMNS_LIST.join(', ')}`);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Valida se há pelo menos um sprint ativo na esteira para evitar "quadro de sprints ativo vazio".
161
+ */
162
+ function validateSprintHealth(sprints = []) {
163
+ const activeSprints = sprints.filter(s => s.state === 'active');
164
+ const futureSprints = sprints.filter(s => s.state === 'future');
165
+ const closedSprints = sprints.filter(s => s.state === 'closed');
166
+
167
+ return {
168
+ totalSprints: sprints.length,
169
+ activeCount: activeSprints.length,
170
+ futureCount: futureSprints.length,
171
+ closedCount: closedSprints.length,
172
+ hasActiveSprint: activeSprints.length > 0,
173
+ warning: activeSprints.length === 0
174
+ ? 'ALERTA DE FLUXO: Nenhuma sprint está ativa no Jira. A tela de Sprints Ativos ficará em branco até que uma sprint seja iniciada.'
175
+ : null
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Avalia candidato do backlog com base no contexto do projeto (Regra 14).
181
+ */
182
+ function evaluateFullDevCandidate(candidate, projectContext = {}) {
183
+ const { title = '', description = '' } = candidate;
184
+ const { stack = 'generic', domain = 'general' } = projectContext;
185
+
186
+ const text = `${title} ${description}`.toLowerCase();
187
+ const evaluation = {
188
+ valid: true,
189
+ candidateTitle: sanitizeIssueSummary(title),
190
+ reasons: [],
191
+ recommendedAction: 'PROCEED_TO_IN_PROGRESS'
192
+ };
193
+
194
+ const overengineeringKeywords = ['blockchain', 'smart contract', 'quantum computing', 'microservices mesh'];
195
+ const matchedOver = overengineeringKeywords.filter(k => text.includes(k));
196
+ if (matchedOver.length > 0 && !String(domain).toLowerCase().includes('crypto')) {
197
+ evaluation.valid = false;
198
+ evaluation.reasons.push(`Overengineering ou desconexão com domínio (${domain}): detectado [${matchedOver.join(', ')}]`);
199
+ evaluation.recommendedAction = 'DROP_OR_REJECT';
200
+ }
201
+
202
+ if (text.includes('kubernetes') && (stack.includes('cli') || stack.includes('desktop'))) {
203
+ evaluation.valid = false;
204
+ evaluation.reasons.push(`Incompatibilidade com stack (${stack}): infraestrutura desproporcional`);
205
+ evaluation.recommendedAction = 'DROP_OR_REJECT';
206
+ }
207
+
208
+ return evaluation;
209
+ }
210
+
211
+ /**
212
+ * Orquestra o ciclo do Modo Full Developer baseado nos 3 Kanbans canônicos.
213
+ */
214
+ function processFullDevPipeline(tasks = [], projectContext = {}, maxWip = 2) {
215
+ const todoTasks = tasks.filter(t => resolveCanonicalColumn(t.status || t.statusName, t.statusCategory) === CANONICAL_KANBANS.TODO);
216
+ const inProgressTasks = tasks.filter(t => resolveCanonicalColumn(t.status || t.statusName, t.statusCategory) === CANONICAL_KANBANS.IN_PROGRESS);
217
+ const doneTasks = tasks.filter(t => resolveCanonicalColumn(t.status || t.statusName, t.statusCategory) === CANONICAL_KANBANS.DONE);
218
+
219
+ const wipStatus = checkWipLimit(inProgressTasks.length, maxWip);
220
+
221
+ const results = {
222
+ totalTasks: tasks.length,
223
+ counts: {
224
+ todo: todoTasks.length,
225
+ inProgress: inProgressTasks.length,
226
+ done: doneTasks.length
227
+ },
228
+ wipStatus,
229
+ activeTask: inProgressTasks[0] || null,
230
+ activeTasks: inProgressTasks,
231
+ nextApprovedTask: null,
232
+ rejectedTasks: [],
233
+ lifecyclePlan: []
234
+ };
235
+
236
+ if (wipStatus.isSaturated) {
237
+ results.lifecyclePlan.push({
238
+ step: 'CONCLUDE_ACTIVE_TASKS',
239
+ message: wipStatus.alertMessage
240
+ });
241
+ return results;
242
+ }
243
+
244
+ for (const candidate of todoTasks) {
245
+ const evalRes = evaluateFullDevCandidate(candidate, projectContext);
246
+ if (evalRes.valid && !results.nextApprovedTask) {
247
+ results.nextApprovedTask = candidate;
248
+ results.lifecyclePlan.push({
249
+ step: 'TRANSITION_AND_START',
250
+ taskKey: candidate.key || candidate.id,
251
+ cleanSummary: evalRes.candidateTitle,
252
+ from: CANONICAL_KANBANS.TODO,
253
+ to: CANONICAL_KANBANS.IN_PROGRESS,
254
+ message: 'Ideia/tarefa aprovada pelo filtro de sanidade (Regra 14). Transicionar para "Em andamento" e iniciar ciclo.'
255
+ });
256
+ } else if (!evalRes.valid) {
257
+ results.rejectedTasks.push({
258
+ taskKey: candidate.key || candidate.id,
259
+ reasons: evalRes.reasons
260
+ });
261
+ }
262
+ }
263
+
264
+ return results;
265
+ }
266
+
267
+ // ==========================================
268
+ // 2. TIME TRACKING & WORKLOGS
269
+ // ==========================================
270
+
271
+ function extractIssueKeys(text) {
272
+ if (!text) return [];
273
+ const matches = text.match(/\b[A-Z]{2,10}-\d+\b/g) || [];
274
+ return [...new Set(matches.map(k => k.toUpperCase()))];
275
+ }
276
+
277
+ async function getExistingWorklogs(credentials, issueKey) {
278
+ const res = await requestAtlassian(credentials, 'GET', `/rest/api/3/issue/${issueKey}/worklog`);
279
+ if (res.status === 200 && res.body?.worklogs) {
280
+ return res.body.worklogs;
281
+ }
282
+ return [];
283
+ }
284
+
285
+ async function logWorkTime(credentials, issueKey, timeSpentSeconds, commentText) {
286
+ const startedTime = new Date().toISOString().replace('Z', '+0000');
287
+ const payload = {
288
+ timeSpentSeconds,
289
+ started: startedTime,
290
+ comment: {
291
+ type: 'doc',
292
+ version: 1,
293
+ content: [
294
+ {
295
+ type: 'paragraph',
296
+ content: [{ type: 'text', text: commentText }]
297
+ }
298
+ ]
299
+ }
300
+ };
301
+
302
+ return requestAtlassian(credentials, 'POST', `/rest/api/3/issue/${issueKey}/worklog`, payload);
303
+ }
304
+
305
+ async function ensureInProgress(credentials, issueKey) {
306
+ try {
307
+ const issueRes = await requestAtlassian(credentials, 'GET', `/rest/api/3/issue/${issueKey}?fields=status`);
308
+ if (issueRes.status === 200 && issueRes.body?.fields?.status) {
309
+ const currentStatus = issueRes.body.fields.status.name.toLowerCase();
310
+ if (currentStatus.includes('backlog') || currentStatus.includes('fazer') || currentStatus.includes('to do')) {
311
+ await requestAtlassian(credentials, 'POST', `/rest/api/3/issue/${issueKey}/transitions`, {
312
+ transition: { id: '31' }
313
+ });
314
+ console.log(`[Jira Manager] 🔄 Card ${issueKey} transicionado para "Em andamento".`);
315
+ }
316
+ }
317
+ } catch (err) {
318
+ console.warn(`[Jira Manager] Aviso ao checar status de ${issueKey}:`, err.message);
319
+ }
320
+ }
321
+
322
+ async function trackIssueTime(credentials, key, gitMeta) {
323
+ await ensureInProgress(credentials, key);
324
+
325
+ const existing = await getExistingWorklogs(credentials, key);
326
+ const tag = `[commit:${gitMeta.shortSha}]`;
327
+ const alreadyLogged = existing.some(wl => {
328
+ const text = JSON.stringify(wl.comment || {});
329
+ return text.includes(tag);
330
+ });
331
+
332
+ if (alreadyLogged) {
333
+ console.log(`[Jira Manager] ℹ Tempo já registrado anteriormente para o commit ${gitMeta.shortSha} no card ${key}.`);
334
+ return { key, status: 'already_logged' };
335
+ }
336
+
337
+ const timeSpentSeconds = 900; // 15 minutos padrão
338
+ const comment = `Tempo de andamento registrado automaticamente via esteira de CI/CD.\nAutor: ${gitMeta.authorName || 'DevSecOps Bot'}\nCommit: ${gitMeta.shortSha} (${gitMeta.commitMessage.split('\n')[0]})\n${tag}`;
339
+
340
+ const res = await logWorkTime(credentials, key, timeSpentSeconds, comment);
341
+ if (res.status === 201) {
342
+ console.log(`[Jira Manager] ✅ 15m de andamento registrados no Jira para ${key} (Commit ${gitMeta.shortSha}).`);
343
+ return { key, status: 'logged', timeSpentSeconds };
344
+ } else {
345
+ console.warn(`[Jira Manager] ⚠️ Falha ao registrar tempo para ${key}: HTTP ${res.status}`);
346
+ return { key, status: 'failed', code: res.status };
347
+ }
348
+ }
349
+
350
+ async function runTimeTracking() {
351
+ console.log('--- Início da Execução do Jira Time Tracking Automático ---');
352
+ const credentials = getAtlassianCredentials();
353
+ if (!credentials.isConfigured) {
354
+ console.log('[Jira Manager] ℹ Credenciais Atlassian não configuradas. Pulando registro de tempo.');
355
+ return;
356
+ }
357
+
358
+ const gitMeta = getGitMetadata();
359
+ const keys = [
360
+ ...extractIssueKeys(gitMeta.branch),
361
+ ...extractIssueKeys(gitMeta.commitMessage)
362
+ ];
363
+
364
+ const uniqueKeys = [...new Set(keys)];
365
+ if (uniqueKeys.length === 0) {
366
+ console.log('[Jira Manager] ℹ Nenhuma chave Jira identificada na branch ou mensagem de commit.');
367
+ return;
368
+ }
369
+
370
+ console.log(`[Jira Manager] Chaves detectadas: ${uniqueKeys.join(', ')}`);
371
+ for (const key of uniqueKeys) {
372
+ try {
373
+ await trackIssueTime(credentials, key, gitMeta);
374
+ } catch (err) {
375
+ console.error(`[Jira Manager] ❌ Erro ao processar tempo para ${key}:`, err.message);
376
+ }
377
+ }
378
+ }
379
+
380
+ if (require.main === module) {
381
+ const args = process.argv.slice(2);
382
+ if (args.includes('--track')) {
383
+ runTimeTracking().catch(console.error);
384
+ } else {
385
+ console.log('Jira Manager operacional. Use --track para executar apontamento de horas.');
386
+ }
387
+ }
388
+
389
+ module.exports = {
390
+ // Kanban
391
+ CANONICAL_KANBANS,
392
+ CANONICAL_COLUMNS_LIST,
393
+ sanitizeIssueSummary,
394
+ checkWipLimit,
395
+ resolveCanonicalColumn,
396
+ standardizeBoardColumns,
397
+ buildCanonicalJql,
398
+ validateSprintHealth,
399
+ evaluateFullDevCandidate,
400
+ processFullDevPipeline,
401
+ // Time Tracking
402
+ extractIssueKeys,
403
+ getExistingWorklogs,
404
+ logWorkTime,
405
+ ensureInProgress,
406
+ trackIssueTime,
407
+ runTimeTracking
408
+ };
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/magic_prompt_generator.js
4
+ *
5
+ * Magic Prompt Generator & Web Bookmarklet (AGDEV-27 / I-13).
6
+ * Gera prompt autocontido de ativação instantânea (< 900 tokens) e bookmarklet
7
+ * web para injeção em 1 clique no ChatGPT, Claude.ai, Gemini e DeepSeek Web.
8
+ */
9
+
10
+ const fs = require('node:fs');
11
+ const path = require('node:path');
12
+
13
+ class MagicPromptGenerator {
14
+ constructor(baseDir = path.resolve(__dirname, '..')) {
15
+ this.baseDir = baseDir;
16
+ }
17
+
18
+ generateMagicPrompt(target = 'generic') {
19
+ const prompt = `=== AGENT ORCHESTRATOR & SUÍTE MULTI-AGENTE (UNIVERSAL INSTANT ACTIVATION) ===
20
+ [DIRECTIVE]: You are now running the canonical Agent Suite (Orchestrator, DevSecOps, QA, Scrum, SRE & UI/UX). Assume 100% adherence to the rules below for all responses:
21
+
22
+ [THE 14 GOLDEN ENGINEERING RULES & RULE 15 INVIOLABILITY]:
23
+ 1. TDD First: Write/verify unit tests BEFORE modifying production code.
24
+ 2. Branch by Card & Strict Commit: Dedicated branch <TICKET>. Commit strictly [<TICKET>] - <Ação em português>. Conventional Commits e commits sem ticket banidos.
25
+ 3. Formal PR & CI: Every merge requires a formal Pull Request with 100% green CI checks.
26
+ 4. Zero Secrets (TruffleHog): Never commit credentials, .env files, private keys or tokens.
27
+ 5. DevSecOps OWASP: Strictly prevent CWE-78 (Cmd Injection), CWE-79 (XSS), CWE-89 (SQLi), CWE-116, CWE-1333 (ReDoS).
28
+ 6. Native Telemetry: Expose standard Prometheus metrics /metrics (CPU, RSS, Heap, latency).
29
+ 7. Dual-Layer Output Protocol: Keep chat output concise, short bullet points separated by a DOUBLE BLANK LINE (\\n\\n) with clickable markdown links. Externalize 15-section dense reports to tickets/ADRs.
30
+ 8. Central Knowledge: Record architectural decisions in ADRs. Keep project documentation living and 100% faithful to production.
31
+ 9. Issue Tracker: Move card to 'In progress' before starting; close with dense technical summary.
32
+ 10. Zero Assumptions: Clarify ambiguities or inspect code before proposing solutions.
33
+ 11. Doc Integrity: Preserve existing docstrings, comments and public API contracts.
34
+ 12. Operational Safety: Never execute destructive unconfirmed commands (rm -rf, hard reset).
35
+ 13. Cold Review (AGENT_QA.md): Independently audit diffs with cold skepticism; provide breaking tests.
36
+ 14. Ideation Sanity Filter: Every suggested idea in docs/IDEAS.md must strictly match the project's actual stack, domain, and architecture.
37
+ 15. Rule 15 Inviolability: Zero silent failures. Requirements and credentials (GitHub CLI, Jira, Confluence) are mandatory.
38
+
39
+ [OPERATIONAL MODES]:
40
+ - modo: orchestrator (central coordination, triage and multi-agent dispatch)
41
+ - modo: devsecops (secure, robust code, clean architecture and CI/CD)
42
+ - modo: qa (adversarial cold review, bug hunting and breaking tests)
43
+ - modo: scrum (Kaizen/PDCA continuous improvement without overengineering)
44
+ - modo: sre (SRE, telemetry, incident diagnosis and self-healing)
45
+ - modo: uiux (design system, accessibility WCAG 2.1 AA, UX ergonomics, design tokens)
46
+ - modo: full-dev (autonomous sequential idea implementation loop)
47
+
48
+ [ACTIVATION ACKNOWLEDGEMENT]:
49
+ Acknowledge activation with a 2-line executive greeting:
50
+ "⚡ Suíte Multi-Agente universalmente ativada e pronta para qualquer demanda.
51
+ Informe a stack e solicite credenciais Atlassian (Jira/Confluence) e GitHub CLI se for nova máquina ou projeto."`;
52
+
53
+ return prompt;
54
+ }
55
+
56
+ generateBookmarklet() {
57
+ const rawPrompt = this.generateMagicPrompt();
58
+ const encodedPrompt = JSON.stringify(rawPrompt);
59
+
60
+ // Bookmarklet inline seguro que detecta o campo de chat e copia para o clipboard
61
+ const script = `javascript:(function(){
62
+ var promptText = ${encodedPrompt};
63
+
64
+ if (navigator.clipboard && navigator.clipboard.writeText) {
65
+ navigator.clipboard.writeText(promptText).then(function() {
66
+ showToast("⚡ Suíte Multi-Agente copiada! Cole (Ctrl+V) no chat da IA.");
67
+ }).catch(function() {
68
+ fallbackPrompt(promptText);
69
+ });
70
+ } else {
71
+ fallbackPrompt(promptText);
72
+ }
73
+
74
+ var inputEl = document.querySelector('#prompt-textarea') ||
75
+ document.querySelector('div[contenteditable="true"]') ||
76
+ document.querySelector('textarea');
77
+ if (inputEl) {
78
+ if (inputEl.tagName === 'TEXTAREA') {
79
+ inputEl.value = promptText;
80
+ inputEl.dispatchEvent(new Event('input', { bubbles: true }));
81
+ } else if (inputEl.isContentEditable) {
82
+ inputEl.innerText = promptText;
83
+ inputEl.dispatchEvent(new Event('input', { bubbles: true }));
84
+ }
85
+ }
86
+
87
+ function showToast(msg) {
88
+ var toast = document.createElement('div');
89
+ toast.innerText = msg;
90
+ toast.style.position = 'fixed';
91
+ toast.style.top = '20px';
92
+ toast.style.right = '20px';
93
+ toast.style.backgroundColor = '#0f172a';
94
+ toast.style.color = '#38bdf8';
95
+ toast.style.padding = '14px 20px';
96
+ toast.style.borderRadius = '8px';
97
+ toast.style.boxShadow = '0 10px 25px rgba(0,0,0,0.5)';
98
+ toast.style.zIndex = '999999';
99
+ toast.style.fontFamily = 'system-ui, sans-serif';
100
+ toast.style.fontSize = '14px';
101
+ toast.style.border = '1px solid #38bdf8';
102
+ document.body.appendChild(toast);
103
+ setTimeout(function() { toast.remove(); }, 3500);
104
+ }
105
+
106
+ function fallbackPrompt(text) {
107
+ window.prompt("Copie o Magic Prompt abaixo:", text);
108
+ }
109
+ })();`.replace(/\n\s*/g, '');
110
+
111
+ return script;
112
+ }
113
+
114
+ estimateTokens(text) {
115
+ // Estimativa canônica de ~4 caracteres por token
116
+ return Math.ceil(text.length / 4);
117
+ }
118
+
119
+ exportArtifacts(targetDir = path.join(this.baseDir, 'dist', 'magic_prompt'), options = {}) {
120
+ const dryRun = Boolean(options.dryRun);
121
+ const magicPrompt = this.generateMagicPrompt();
122
+ const bookmarklet = this.generateBookmarklet();
123
+ const tokens = this.estimateTokens(magicPrompt);
124
+
125
+ const htmlContent = `<!DOCTYPE html>
126
+ <html lang="pt-BR">
127
+ <head>
128
+ <meta charset="UTF-8">
129
+ <title>Suíte Multi-Agente — Magic Prompt & Bookmarklet</title>
130
+ <style>
131
+ body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #f8fafc; padding: 40px; line-height: 1.6; }
132
+ .card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 24px; max-width: 800px; margin: 0 auto 30px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); }
133
+ h1 { color: #38bdf8; margin-top: 0; }
134
+ pre { background: #090d16; padding: 16px; border-radius: 8px; overflow-x: auto; font-size: 13px; color: #cbd5e1; border: 1px solid #1e293b; }
135
+ .btn { display: inline-block; background: #0284c7; color: white; padding: 12px 20px; border-radius: 8px; text-decoration: none; font-weight: bold; cursor: grab; }
136
+ .btn:hover { background: #0369a1; }
137
+ </style>
138
+ </head>
139
+ <body>
140
+ <div class="card">
141
+ <h1>⚡ Suíte Multi-Agente — Ativação Instantânea</h1>
142
+ <p>Arraste o botão abaixo para a sua <strong>Barra de Favoritos</strong> do navegador. Ao clicar nele em qualquer aba do ChatGPT, Claude, Gemini ou DeepSeek, a suíte de agentes será ativada instantaneamente!</p>
143
+ <p style="margin: 30px 0;">
144
+ <a class="btn" href="${bookmarklet}">⚡ Ativar Suíte Multi-Agente</a>
145
+ </p>
146
+ <small style="color: #94a3b8;">Dica: você também pode clicar com o botão direito e selecionar "Adicionar aos favoritos".</small>
147
+ </div>
148
+
149
+ <div class="card">
150
+ <h2>📋 Magic Prompt (Cópia Manual — ~${tokens} tokens)</h2>
151
+ <pre id="promptText">${magicPrompt.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</pre>
152
+ </div>
153
+ </body>
154
+ </html>`;
155
+
156
+ const mdContent = `# ⚡ Magic Prompt — Ativação Instantânea da Suíte Multi-Agente
157
+
158
+ Copie o bloco abaixo e cole como primeira mensagem em **qualquer LLM** (ChatGPT, Claude.ai, Gemini, DeepSeek Web, Ollama ou LeChat) para assumir imediatamente 100% das 14 regras de engenharia:
159
+
160
+ \`\`\`text
161
+ ${magicPrompt}
162
+ \`\`\`
163
+
164
+ ---
165
+ *Orçamento semântico: ~${tokens} tokens (limite de 1.000 tokens respeitado com 100% de conformidade).*
166
+ `;
167
+
168
+ const files = [
169
+ { path: path.join(targetDir, 'MAGIC_PROMPT.md'), content: mdContent },
170
+ { path: path.join(targetDir, 'bookmarklet.html'), content: htmlContent }
171
+ ];
172
+
173
+ if (!dryRun) {
174
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
175
+ files.forEach(f => fs.writeFileSync(f.path, f.content, 'utf8'));
176
+ }
177
+
178
+ return {
179
+ status: 'SUCCESS',
180
+ targetDir,
181
+ tokens,
182
+ dryRun,
183
+ filesCount: files.length,
184
+ files: files.map(f => f.path),
185
+ magicPrompt,
186
+ bookmarklet
187
+ };
188
+ }
189
+ }
190
+
191
+ if (require.main === module) {
192
+ const generator = new MagicPromptGenerator();
193
+ const res = generator.exportArtifacts();
194
+ console.log('\n============================================================');
195
+ console.log('⚡ Magic Prompt Generator & Web Bookmarklet (AGDEV-27)');
196
+ console.log('============================================================\n');
197
+ console.log(`✅ Arquivos exportados com sucesso em: ${res.targetDir}`);
198
+ console.log(`📊 Orçamento de Tokens Estimado: ~${res.tokens} tokens (< 1.000 tokens)`);
199
+ console.log(`📄 Arquivos gerados:`, res.files.join(', '));
200
+ console.log('\nO bookmarklet HTML e o MAGIC_PROMPT.md estão prontos para uso!\n');
201
+ }
202
+
203
+ module.exports = {
204
+ MagicPromptGenerator
205
+ };