nexus-core-v3 3.0.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 (232) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/agents/README.md +133 -0
  4. package/agents/_protocol.md +107 -0
  5. package/agents/analyst.md +138 -0
  6. package/agents/architect.md +146 -0
  7. package/agents/data-engineer.md +170 -0
  8. package/agents/dev.md +134 -0
  9. package/agents/devops.md +141 -0
  10. package/agents/nexus-master.md +147 -0
  11. package/agents/pm.md +133 -0
  12. package/agents/po.md +138 -0
  13. package/agents/qa.md +192 -0
  14. package/agents/sm.md +122 -0
  15. package/agents/squad-creator.md +121 -0
  16. package/agents/ux-design-expert.md +165 -0
  17. package/artifact-manifest.json +903 -0
  18. package/bin/nexus.mjs +37 -0
  19. package/checklists/README.md +49 -0
  20. package/checklists/architect-checklist.md +47 -0
  21. package/checklists/change-checklist.md +61 -0
  22. package/checklists/db-predeploy-checklist.md +57 -0
  23. package/checklists/design-quality-checklist.md +57 -0
  24. package/checklists/discovery-checklist.md +36 -0
  25. package/checklists/foundation-checklist.md +39 -0
  26. package/checklists/launch-checklist.md +39 -0
  27. package/checklists/pm-checklist.md +48 -0
  28. package/checklists/po-master-checklist.md +64 -0
  29. package/checklists/reality-check-checklist.md +49 -0
  30. package/checklists/story-dod-checklist.md +52 -0
  31. package/checklists/story-draft-checklist.md +36 -0
  32. package/dist/bin/dashboard.html +279 -0
  33. package/dist/bin/nexus.mjs +20008 -0
  34. package/dist/constitution.yaml +76 -0
  35. package/knowledge/README.md +57 -0
  36. package/knowledge/architecture/architectural-styles-map.md +182 -0
  37. package/knowledge/architecture/design-patterns-gof.md +192 -0
  38. package/knowledge/architecture/distributed-patterns-cheatsheet.md +201 -0
  39. package/knowledge/architecture/saas-subscription-blueprint.md +355 -0
  40. package/knowledge/architecture/system-design-tradeoffs.md +231 -0
  41. package/knowledge/architecture/t3-fullstack-typesafe-stack.md +273 -0
  42. package/knowledge/copy/landing-copy-that-converts.md +168 -0
  43. package/knowledge/data/postgres-indexing-and-tuning.md +263 -0
  44. package/knowledge/data/schema-modeling-decisions.md +273 -0
  45. package/knowledge/data/supabase-rls-patterns.md +316 -0
  46. package/knowledge/data/zero-downtime-migrations.md +308 -0
  47. package/knowledge/devops/cicd-pipeline-best-practices.md +318 -0
  48. package/knowledge/devops/production-dockerfile.md +283 -0
  49. package/knowledge/devops/twelve-factor-app.md +398 -0
  50. package/knowledge/engineering/clean-code-principles.md +429 -0
  51. package/knowledge/engineering/effective-code-review.md +204 -0
  52. package/knowledge/engineering/testing-strategy-beyond-unit.md +307 -0
  53. package/knowledge/governance/risk-matrix.md +56 -0
  54. package/knowledge/integration/mcp-server-selection-matrix.md +235 -0
  55. package/knowledge/marketing/copy-que-converte.md +43 -0
  56. package/knowledge/marketing/funil-e-jornada.md +36 -0
  57. package/knowledge/negocios/proposta-vencedora.md +38 -0
  58. package/knowledge/negocios/roi-e-unit-economics.md +46 -0
  59. package/knowledge/pipeline/1-descobrir.md +26 -0
  60. package/knowledge/pipeline/2-estrategizar.md +26 -0
  61. package/knowledge/pipeline/3-estruturar.md +27 -0
  62. package/knowledge/pipeline/4-construir.md +27 -0
  63. package/knowledge/pipeline/5-endurecer.md +28 -0
  64. package/knowledge/pipeline/6-lancar.md +27 -0
  65. package/knowledge/pipeline/7-operar.md +27 -0
  66. package/knowledge/security/lgpd-conformidade-basica.md +35 -0
  67. package/knowledge/security/owasp-secure-coding-gates.md +220 -0
  68. package/knowledge/security/owasp-top10-threat-assessment.md +287 -0
  69. package/knowledge/security/threat-modeling-stride.md +34 -0
  70. package/knowledge/web-craft/a11y-audit-checklist.md +251 -0
  71. package/knowledge/web-craft/accessible-component-patterns.md +383 -0
  72. package/knowledge/web-craft/anti-ai-look.md +114 -0
  73. package/knowledge/web-craft/design-system-from-code.md +195 -0
  74. package/knowledge/web-craft/intrinsic-css-layout.md +420 -0
  75. package/knowledge/web-craft/style-cloning.md +185 -0
  76. package/knowledge/web-craft/visual-polish-review.md +183 -0
  77. package/package.json +55 -0
  78. package/runbooks/campanha-de-conteudo.md +36 -0
  79. package/runbooks/feature-em-projeto-existente.md +37 -0
  80. package/runbooks/mvp-startup.md +38 -0
  81. package/runbooks/resposta-a-incidente.md +37 -0
  82. package/squads/exemplo-conteudo/agents/editor-chefe.md +48 -0
  83. package/squads/exemplo-conteudo/agents/pesquisador.md +44 -0
  84. package/squads/exemplo-conteudo/agents/redator.md +45 -0
  85. package/squads/exemplo-conteudo/knowledge/estilo-editorial.md +21 -0
  86. package/squads/exemplo-conteudo/squad.yaml +19 -0
  87. package/squads/exemplo-conteudo/tasks/pesquisar-fontes.md +26 -0
  88. package/squads/exemplo-conteudo/tasks/planejar-pauta.md +27 -0
  89. package/squads/exemplo-conteudo/tasks/redigir-artigo.md +26 -0
  90. package/squads/exemplo-conteudo/tasks/revisar-artigo.md +27 -0
  91. package/squads/marketing/agents/analista.md +56 -0
  92. package/squads/marketing/agents/chefe-marketing.md +65 -0
  93. package/squads/marketing/agents/conteudo.md +55 -0
  94. package/squads/marketing/agents/copy.md +55 -0
  95. package/squads/marketing/agents/growth.md +56 -0
  96. package/squads/marketing/agents/social.md +55 -0
  97. package/squads/marketing/squad.yaml +17 -0
  98. package/squads/marketing/tasks/aprovar-campanha.md +43 -0
  99. package/squads/negocios/agents/chefe-negocios.md +65 -0
  100. package/squads/negocios/agents/financas-roi.md +55 -0
  101. package/squads/negocios/agents/suporte.md +55 -0
  102. package/squads/negocios/agents/vendas-proposta.md +56 -0
  103. package/squads/negocios/squad.yaml +17 -0
  104. package/squads/negocios/tasks/aprovar-proposta.md +40 -0
  105. package/squads/security/agents/appsec-reviewer.md +59 -0
  106. package/squads/security/agents/chefe-seguranca.md +65 -0
  107. package/squads/security/agents/compliance-auditor.md +60 -0
  108. package/squads/security/agents/threat-modeler.md +60 -0
  109. package/squads/security/squad.yaml +20 -0
  110. package/squads/security/tasks/aprovar-gate-seguranca.md +42 -0
  111. package/squads/security/tasks/emitir-parecer-conformidade.md +42 -0
  112. package/tasks/README.md +72 -0
  113. package/tasks/accessibility-wcag-checklist.md +69 -0
  114. package/tasks/advanced-elicitation.md +42 -0
  115. package/tasks/analyze-performance.md +54 -0
  116. package/tasks/analyze-project-structure.md +59 -0
  117. package/tasks/apply-qa-fixes.md +57 -0
  118. package/tasks/architect-analyze-impact.md +62 -0
  119. package/tasks/archive-squad.md +52 -0
  120. package/tasks/audit-codebase.md +53 -0
  121. package/tasks/build-component.md +61 -0
  122. package/tasks/calculate-roi.md +63 -0
  123. package/tasks/ci-cd-configuration.md +51 -0
  124. package/tasks/collect-visual-evidence.md +62 -0
  125. package/tasks/compose-molecule.md +57 -0
  126. package/tasks/consolidate-patterns.md +54 -0
  127. package/tasks/create-brownfield-prd.md +54 -0
  128. package/tasks/create-competitor-analysis.md +42 -0
  129. package/tasks/create-deep-research-prompt.md +62 -0
  130. package/tasks/create-doc.md +62 -0
  131. package/tasks/create-epic.md +49 -0
  132. package/tasks/create-front-end-spec.md +56 -0
  133. package/tasks/create-migration-plan.md +57 -0
  134. package/tasks/create-next-story.md +66 -0
  135. package/tasks/create-prd.md +53 -0
  136. package/tasks/create-project-brief.md +47 -0
  137. package/tasks/create-rls-policies.md +59 -0
  138. package/tasks/create-schema.md +57 -0
  139. package/tasks/create-service.md +55 -0
  140. package/tasks/create-squad.md +100 -0
  141. package/tasks/create-suite.md +62 -0
  142. package/tasks/db-apply-migration.md +56 -0
  143. package/tasks/db-domain-modeling.md +57 -0
  144. package/tasks/db-dry-run.md +50 -0
  145. package/tasks/db-env-check.md +57 -0
  146. package/tasks/db-load-csv.md +54 -0
  147. package/tasks/db-policy-apply.md +58 -0
  148. package/tasks/db-rollback.md +51 -0
  149. package/tasks/db-run-sql.md +61 -0
  150. package/tasks/db-seed.md +52 -0
  151. package/tasks/db-smoke-test.md +51 -0
  152. package/tasks/db-snapshot.md +48 -0
  153. package/tasks/db-verify-order.md +49 -0
  154. package/tasks/deliberate.md +46 -0
  155. package/tasks/design-indexes.md +59 -0
  156. package/tasks/dev-develop-story.md +61 -0
  157. package/tasks/document-project.md +59 -0
  158. package/tasks/execute-checklist.md +57 -0
  159. package/tasks/execute-epic-plan.md +52 -0
  160. package/tasks/execute-subtask.md +51 -0
  161. package/tasks/extend-pattern.md +63 -0
  162. package/tasks/extend-squad.md +60 -0
  163. package/tasks/extract-patterns.md +64 -0
  164. package/tasks/extract-tokens.md +59 -0
  165. package/tasks/facilitate-brainstorming-session.md +42 -0
  166. package/tasks/generate-ai-frontend-prompt.md +57 -0
  167. package/tasks/generate-documentation.md +60 -0
  168. package/tasks/generate-migration-strategy.md +57 -0
  169. package/tasks/generate-shock-report.md +56 -0
  170. package/tasks/mcp-management.md +66 -0
  171. package/tasks/orchestrate.md +50 -0
  172. package/tasks/perform-market-research.md +42 -0
  173. package/tasks/plan-create-context.md +57 -0
  174. package/tasks/plan-create-implementation.md +58 -0
  175. package/tasks/po-close-story.md +60 -0
  176. package/tasks/po-manage-story-backlog.md +59 -0
  177. package/tasks/po-pull-story.md +60 -0
  178. package/tasks/po-sync-story.md +59 -0
  179. package/tasks/pr-automation.md +50 -0
  180. package/tasks/pre-push-quality-gate.md +54 -0
  181. package/tasks/push.md +53 -0
  182. package/tasks/qa-browser-console-check.md +52 -0
  183. package/tasks/qa-create-fix-request.md +58 -0
  184. package/tasks/qa-evidence-requirements.md +55 -0
  185. package/tasks/qa-false-positive-detection.md +55 -0
  186. package/tasks/qa-fix-issues.md +55 -0
  187. package/tasks/qa-gate.md +53 -0
  188. package/tasks/qa-migration-validation.md +58 -0
  189. package/tasks/qa-nfr-assess.md +45 -0
  190. package/tasks/qa-review-story.md +56 -0
  191. package/tasks/qa-risk-profile.md +45 -0
  192. package/tasks/qa-security-checklist.md +64 -0
  193. package/tasks/qa-test-design.md +47 -0
  194. package/tasks/qa-trace-requirements.md +48 -0
  195. package/tasks/release-management.md +53 -0
  196. package/tasks/repository-cleanup.md +61 -0
  197. package/tasks/route.md +44 -0
  198. package/tasks/run-tests.md +50 -0
  199. package/tasks/security-audit.md +54 -0
  200. package/tasks/setup-database.md +60 -0
  201. package/tasks/setup-design-system.md +60 -0
  202. package/tasks/shard-doc.md +60 -0
  203. package/tasks/spec-assess-complexity.md +55 -0
  204. package/tasks/spec-critique.md +64 -0
  205. package/tasks/spec-gather-requirements.md +48 -0
  206. package/tasks/spec-research-dependencies.md +42 -0
  207. package/tasks/spec-write-spec.md +50 -0
  208. package/tasks/test-as-user.md +52 -0
  209. package/tasks/ux-create-wireframe.md +54 -0
  210. package/tasks/ux-user-research.md +55 -0
  211. package/tasks/validate-next-story.md +61 -0
  212. package/tasks/validate-squad.md +55 -0
  213. package/tasks/verify-subtask.md +52 -0
  214. package/tasks/version-management.md +45 -0
  215. package/templates/README.md +47 -0
  216. package/templates/architecture-tmpl.md +115 -0
  217. package/templates/competitor-analysis-tmpl.md +87 -0
  218. package/templates/epic-tmpl.md +83 -0
  219. package/templates/front-end-spec-tmpl.md +110 -0
  220. package/templates/market-research-tmpl.md +98 -0
  221. package/templates/migration-plan-tmpl.md +92 -0
  222. package/templates/prd-tmpl.md +95 -0
  223. package/templates/project-brief-tmpl.md +100 -0
  224. package/templates/qa-verdict-tmpl.md +73 -0
  225. package/templates/rls-policies-tmpl.md +93 -0
  226. package/templates/schema-design-tmpl.md +107 -0
  227. package/templates/spec-tmpl.md +88 -0
  228. package/templates/squad/agent-dna-tmpl.md +72 -0
  229. package/templates/squad/chief-dna-tmpl.md +98 -0
  230. package/templates/squad/squad-task-tmpl.md +50 -0
  231. package/templates/squad/squad-yaml-tmpl.md +47 -0
  232. package/templates/story-tmpl.md +63 -0
@@ -0,0 +1,52 @@
1
+ ---
2
+ id: story-dod-checklist
3
+ kind: checklist
4
+ agent: dev
5
+ gate: Definition of Done
6
+ decision: "qualquer item crítico (1-7) FALHO → NÃO PRONTO; todos OK → Ready for Review"
7
+ ---
8
+
9
+ # Story DoD Checklist (Definition of Done)
10
+
11
+ > O @dev (Dex) roda isto antes de setar a story como `Ready for Review`. Não é sobre "tentei" — é sobre
12
+ > "entreguei e provei". Veredito binário: **READY FOR REVIEW** (todos os críticos passam) ou **NÃO PRONTO**
13
+ > (lista do que falta). Story que vai para o @qa sem passar aqui volta como retrabalho.
14
+
15
+ ## Críticos — qualquer falha aqui = NÃO PRONTO
16
+
17
+ - [ ] 1. **Todos os ACs atendidos** — cada critério de aceite da story tem implementação correspondente;
18
+ nenhum AC ficou sem cobertura ou foi parcialmente entregue.
19
+ - [ ] 2. **Todas as tasks/subtasks `[x]`** — cada checkbox marcado tem código + teste por trás; nenhum
20
+ `[x]` marcado por adiantamento ("testo depois").
21
+ - [ ] 3. **Testes escritos e verdes** — caminho feliz + casos de erro/borda cobertos; a suíte inteira
22
+ (`*run-tests`) roda e passa, sem teste pulado/skip sem justificativa.
23
+ - [ ] 4. **Lint e validações passando** — lint/typecheck verdes; zero erro novo introduzido por esta story.
24
+ - [ ] 5. **Regressão completa rodada** — TODOS os testes do projeto executados (não só os da story) e
25
+ nenhuma regressão; nada quebrou no que já funcionava.
26
+ - [ ] 6. **File List completa e correta** — todo arquivo criado, modificado ou deletado está listado;
27
+ nada de tocar arquivo fora da File List sem registrá-lo.
28
+ - [ ] 7. **Sem invenção / sem dependência não aprovada** (Constituição Art. IV) — nada implementado fora
29
+ do escopo da story; nenhuma dependência nova entrou sem aprovação explícita.
30
+
31
+ ## Não-críticos — falha vira nota em Completion Notes, não bloqueio
32
+
33
+ - [ ] 8. **CodeRabbit rodado** — review automático executado; CRITICAL corrigido no lugar (máx. 2
34
+ iterações). Achados de menor severidade ficam documentados, não pendentes silenciosos.
35
+ - [ ] 9. **Menor caminho respeitado** — sem super-construção; reusou padrões/utilitários já existentes
36
+ antes de criar estrutura nova.
37
+ - [ ] 10. **Dev Agent Record preenchido** — Debug Log, Completion Notes e Change Log refletem o que foi
38
+ feito, decisões tomadas e qualquer débito conhecido deixado para o @qa.
39
+ - [ ] 11. **Fronteiras respeitadas** — só editei minhas seções da story (Dev Agent Record, checkboxes,
40
+ File List, Status); AC/escopo/Dev Notes/Testing intocados. Schema → @data-engineer; UI →
41
+ @ux-design-expert; push/PR → @devops.
42
+
43
+ ## Decisão
44
+
45
+ **Veredito:** READY FOR REVIEW / NÃO PRONTO
46
+ **Críticos (1-7):** {n}/7 OK
47
+ **Não-críticos (8-11):** {n}/4 OK
48
+ **Pendências (se NÃO PRONTO ou notas para o @qa):**
49
+ - {item — o que falta ou que débito conhecido segue para review}
50
+
51
+ > Se READY FOR REVIEW: seto `Status: Ready for Review` e HALTO. Eu **não** dou push — quem sobe é o @devops.
52
+ > Se NÃO PRONTO: volto ao loop de implementação; não passo problema crítico conhecido adiante.
@@ -0,0 +1,36 @@
1
+ ---
2
+ id: story-draft-checklist
3
+ kind: checklist
4
+ agent: po
5
+ gate: Definition of Ready
6
+ decision: ">=7/10 → GO; <7 → NO-GO com correções obrigatórias"
7
+ ---
8
+
9
+ # Story Draft Checklist (Definition of Ready)
10
+
11
+ > O @po roda isto antes de a story ir para o @dev. Veredito: **GO** (≥7 dos 10) ou **NO-GO** (lista de
12
+ > correções). Story que entra em desenvolvimento sem passar aqui é retrabalho garantido.
13
+
14
+ ## Critérios (10 pontos)
15
+
16
+ - [ ] 1. **Título e escopo claros** — dá pra dizer o que a story entrega em uma frase.
17
+ - [ ] 2. **Story bem formada** — "Como {persona}, quero {ação}, para {benefício}" preenchida e coerente.
18
+ - [ ] 3. **ACs testáveis** — cada critério de aceite é verificável com sim/não; nada de "funciona bem".
19
+ - [ ] 4. **ACs rastreáveis** — cada AC liga a um FR/NFR/objetivo; nenhum AC inventado (No Invention).
20
+ - [ ] 5. **Tasks/Subtasks presentes** — há decomposição técnica e cada uma aponta para o(s) AC(s).
21
+ - [ ] 6. **Dev Notes autossuficientes** — o @dev consegue implementar sem abrir outros docs; afirmações
22
+ técnicas citam a fonte (`docs/architecture/...`, `docs/specs/...`).
23
+ - [ ] 7. **Seção Testing definida** — estratégia + casos (incl. erro/edge) + como rodar.
24
+ - [ ] 8. **Dependências mapeadas** — stories/serviços/dados de que esta depende estão identificados.
25
+ - [ ] 9. **Dono certo** — o trabalho cabe na lane de um agente; o que sai (schema, UI, subida) está
26
+ delegado ao dono (@data-engineer / @ux-design-expert / @devops).
27
+ - [ ] 10. **Tamanho sensato** — a story cabe num ciclo; se é grande demais, o @sm fatia antes.
28
+
29
+ ## Decisão
30
+
31
+ **Veredito:** GO (≥7) / NO-GO (<7)
32
+ **Pontuação:** {x}/10
33
+ **Correções obrigatórias (se NO-GO):**
34
+ - {item — o que falta para virar GO}
35
+
36
+ > NO-GO não é reprovação: é a lista exata do que o @sm ajusta para a story ficar pronta.
@@ -0,0 +1,279 @@
1
+ <!doctype html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Nexus</title>
7
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%230f766e'/%3E%3Ctext x='16' y='23' font-size='20' font-family='sans-serif' font-weight='700' fill='white' text-anchor='middle'%3En%3C/text%3E%3C/svg%3E" />
8
+ <style>
9
+ /* ── design tokens (claro/escuro por prefers-color-scheme) ───────────────── */
10
+ :root {
11
+ color-scheme: light dark;
12
+ --bg: #faf9f7;
13
+ --surface: #ffffff;
14
+ --surface-2: #f4f2ef;
15
+ --text: #1c1917;
16
+ --muted: #78716c;
17
+ --border: #e7e4df;
18
+ --accent: #0f766e;
19
+ --accent-ink: #ffffff;
20
+ --ok: #15803d;
21
+ --warn: #b45309;
22
+ --bad: #b91c1c;
23
+ --mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, monospace;
24
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
25
+ --radius: 10px;
26
+ --shadow: 0 1px 2px rgba(28,25,23,.06), 0 1px 3px rgba(28,25,23,.04);
27
+ }
28
+ @media (prefers-color-scheme: dark) {
29
+ :root {
30
+ --bg: #0c0a09; --surface: #1c1917; --surface-2: #292524;
31
+ --text: #f5f5f4; --muted: #a8a29e; --border: #292524;
32
+ --accent: #2dd4bf; --accent-ink: #042f2e;
33
+ --ok: #4ade80; --warn: #fbbf24; --bad: #f87171;
34
+ --shadow: none;
35
+ }
36
+ }
37
+ * { box-sizing: border-box; }
38
+ html, body { height: 100%; }
39
+ body {
40
+ margin: 0; font-family: var(--sans); color: var(--text); background: var(--bg);
41
+ font-size: 15px; line-height: 1.55; -webkit-font-smoothing: antialiased;
42
+ display: grid; grid-template-columns: 232px 1fr; min-height: 100vh;
43
+ }
44
+ /* ── barra lateral ────────────────────────────────────────────────────────── */
45
+ aside {
46
+ border-right: 1px solid var(--border); background: var(--surface);
47
+ display: flex; flex-direction: column; padding: 20px 14px; gap: 4px; position: sticky; top: 0; height: 100vh;
48
+ }
49
+ .brand { display: flex; align-items: baseline; gap: 3px; padding: 6px 10px 18px; }
50
+ .brand b { font-size: 22px; font-weight: 700; letter-spacing: -.02em; }
51
+ .brand .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); align-self: center; margin-left: 2px; }
52
+ .brand span { font-size: 11px; color: var(--muted); margin-left: auto; text-transform: uppercase; letter-spacing: .08em; }
53
+ nav { display: flex; flex-direction: column; gap: 2px; }
54
+ nav button {
55
+ appearance: none; border: 0; background: transparent; color: var(--text); font: inherit;
56
+ text-align: left; padding: 8px 10px; border-radius: 8px; cursor: pointer; display: flex; gap: 10px; align-items: center;
57
+ }
58
+ nav button:hover { background: var(--surface-2); }
59
+ nav button[aria-current="true"] { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
60
+ nav .ico { width: 18px; text-align: center; opacity: .9; font-size: 15px; }
61
+ aside footer { margin-top: auto; padding: 10px; font-size: 12px; color: var(--muted); }
62
+ /* ── conteúdo ─────────────────────────────────────────────────────────────── */
63
+ main { padding: 34px clamp(20px, 4vw, 56px); max-width: 1000px; width: 100%; }
64
+ header.page { margin-bottom: 22px; }
65
+ header.page h1 { margin: 0 0 4px; font-size: 24px; letter-spacing: -.02em; }
66
+ header.page p { margin: 0; color: var(--muted); }
67
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 20px; }
68
+ .card + .card { margin-top: 16px; }
69
+ pre.out {
70
+ font-family: var(--mono); font-size: 12.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
71
+ background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; margin: 0; overflow-x: auto;
72
+ }
73
+ button.act, .chip, select, input, textarea {
74
+ font: inherit; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); color: var(--text);
75
+ }
76
+ button.act {
77
+ background: var(--accent); color: var(--accent-ink); border-color: transparent; padding: 9px 16px; font-weight: 600; cursor: pointer;
78
+ }
79
+ button.act:disabled { opacity: .55; cursor: not-allowed; }
80
+ button.ghost { background: var(--surface); color: var(--text); border: 1px solid var(--border); padding: 8px 14px; border-radius: 8px; cursor: pointer; font: inherit; }
81
+ input, select, textarea { padding: 9px 12px; width: 100%; }
82
+ textarea { resize: vertical; min-height: 84px; font-family: var(--sans); }
83
+ label { display: block; font-size: 13px; color: var(--muted); margin: 0 0 5px; }
84
+ .row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
85
+ .field + .field { margin-top: 14px; }
86
+ .chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
87
+ .chip { padding: 7px 12px; cursor: pointer; font-size: 13px; color: var(--muted); }
88
+ .chip:hover { border-color: var(--accent); color: var(--text); }
89
+ .muted { color: var(--muted); }
90
+ .empty { color: var(--muted); text-align: center; padding: 32px 12px; }
91
+ .plist { list-style: none; margin: 0; padding: 0; }
92
+ .plist li { display: flex; justify-content: space-between; align-items: center; padding: 12px 4px; border-bottom: 1px solid var(--border); }
93
+ .plist li:last-child { border-bottom: 0; }
94
+ .plist .p-name { font-weight: 600; }
95
+ .plist .p-path { font-family: var(--mono); font-size: 12px; color: var(--muted); }
96
+ .health li { display: flex; gap: 10px; align-items: baseline; padding: 8px 4px; border-bottom: 1px solid var(--border); list-style: none; }
97
+ .health { margin: 0; padding: 0; }
98
+ .light { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; margin-top: 6px; }
99
+ .light.ok { background: var(--ok); } .light.warn { background: var(--warn); } .light.bad { background: var(--bad); }
100
+ .ev { font-family: var(--mono); font-size: 12.5px; padding: 3px 0; border-bottom: 1px dashed var(--border); }
101
+ .ev .tag { display: inline-block; min-width: 74px; color: var(--accent); font-weight: 600; }
102
+ .spin { color: var(--muted); font-size: 13px; }
103
+ .banner { background: color-mix(in srgb, var(--bad) 12%, var(--surface)); border: 1px solid var(--bad); color: var(--bad); padding: 12px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 14px; }
104
+ @media (max-width: 720px) { body { grid-template-columns: 1fr; } aside { position: static; height: auto; flex-direction: row; flex-wrap: wrap; } aside footer { display: none; } }
105
+ </style>
106
+ </head>
107
+ <body>
108
+ <aside>
109
+ <div class="brand"><b>nexus</b><i class="dot"></i><span>local</span></div>
110
+ <nav id="nav"></nav>
111
+ <footer>Tudo roda na sua máquina, com a sua assinatura.<br />Nada sai daqui.</footer>
112
+ </aside>
113
+ <main>
114
+ <div id="authwarn" class="banner" hidden>Sessão sem token válido — reabra pelo link que o <code>nexus serve</code> imprimiu no terminal.</div>
115
+ <header class="page"><h1 id="ptitle"></h1><p id="psub"></p></header>
116
+ <section id="panel"></section>
117
+ </main>
118
+
119
+ <script>
120
+ 'use strict';
121
+ const token = new URLSearchParams(location.search).get('t') || '';
122
+ const $ = (id) => document.getElementById(id);
123
+ const el = (tag, attrs, ...kids) => {
124
+ const n = document.createElement(tag);
125
+ for (const k in (attrs || {})) {
126
+ if (k === 'class') n.className = attrs[k];
127
+ else if (k === 'html') n.innerHTML = attrs[k];
128
+ else if (k.startsWith('on')) n.addEventListener(k.slice(2), attrs[k]);
129
+ else n.setAttribute(k, attrs[k]);
130
+ }
131
+ for (const kid of kids) n.append(kid && kid.nodeType ? kid : document.createTextNode(kid == null ? '' : String(kid)));
132
+ return n;
133
+ };
134
+ const ESC = String.fromCharCode(27);
135
+ const ansi = new RegExp(ESC + '[[]' + '[0-9;?]*[a-zA-Z]', 'g');
136
+ const clean = (s) => (s || '').replace(ansi, '');
137
+
138
+ async function api(path, opts) {
139
+ opts = opts || {};
140
+ const headers = Object.assign({ 'Authorization': 'Bearer ' + token }, opts.headers || {});
141
+ const res = await fetch(path, Object.assign({}, opts, { headers }));
142
+ if (res.status === 401) { $('authwarn').hidden = false; throw new Error('401'); }
143
+ return res;
144
+ }
145
+ const getJson = async (path) => (await api(path)).json();
146
+
147
+ // ── painéis ────────────────────────────────────────────────────────────────
148
+ const panel = $('panel');
149
+ const setBusy = (msg) => { panel.replaceChildren(el('p', { class: 'spin' }, msg || 'Carregando…')); };
150
+
151
+ async function textPanel(path, empty) {
152
+ setBusy();
153
+ try {
154
+ const d = await getJson(path);
155
+ const t = clean(d.text || '').trim();
156
+ panel.replaceChildren(t ? el('pre', { class: 'out' }, t) : el('div', { class: 'empty' }, empty || 'Nada por aqui ainda.'));
157
+ } catch (e) { if (e.message !== '401') panel.replaceChildren(el('div', { class: 'empty' }, 'Não consegui carregar. Confira a Saúde do sistema.')); }
158
+ }
159
+
160
+ async function saude() {
161
+ setBusy('Checando o ambiente…');
162
+ try {
163
+ const d = await getJson('/api/doctor');
164
+ const lines = clean(d.text || '').split(String.fromCharCode(10)).map((l) => l.trim()).filter(Boolean);
165
+ const ul = el('ul', { class: 'health' });
166
+ for (const line of lines) {
167
+ let cls = 'ok';
168
+ if (/\[(falha|erro|fail)/i.test(line)) cls = 'bad';
169
+ else if (/\[(aviso|warn)/i.test(line)) cls = 'warn';
170
+ const label = line.replace(/^\[[^\]]*\]\s*/, '');
171
+ ul.append(el('li', {}, el('i', { class: 'light ' + cls }), el('span', {}, label)));
172
+ }
173
+ panel.replaceChildren(el('div', { class: 'card' }, ul), el('div', { class: 'row', style: 'margin-top:14px' }, el('button', { class: 'ghost', onclick: saude }, 'Verificar de novo')));
174
+ } catch (e) { if (e.message !== '401') panel.replaceChildren(el('div', { class: 'empty' }, 'Não consegui rodar o diagnóstico.')); }
175
+ }
176
+
177
+ async function projetos() {
178
+ setBusy();
179
+ try {
180
+ const d = await getJson('/api/projects');
181
+ const list = d.projects || [];
182
+ const card = el('div', { class: 'card' });
183
+ if (!list.length) card.append(el('div', { class: 'empty' }, 'Nenhum projeto ainda — crie o primeiro abaixo.'));
184
+ else {
185
+ const ul = el('ul', { class: 'plist' });
186
+ for (const p of list) ul.append(el('li', {}, el('div', {}, el('div', { class: 'p-name' }, p.name || p.path), el('div', { class: 'p-path' }, p.path)), el('span', { class: 'muted' }, p.lastRunAt ? ('última: ' + p.lastRunAt) : 'sem missões')));
187
+ card.append(ul);
188
+ }
189
+ const nameI = el('input', { placeholder: 'nome do projeto', 'aria-label': 'nome do projeto' });
190
+ const out = el('div', { class: 'muted', style: 'margin-top:10px' });
191
+ const create = el('button', {
192
+ class: 'act', onclick: async () => {
193
+ if (!nameI.value.trim()) return;
194
+ create.disabled = true; out.textContent = 'Criando…';
195
+ try {
196
+ const r = await api('/api/projects', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: nameI.value.trim() }) });
197
+ const b = await r.json();
198
+ if (r.ok) { out.textContent = ''; projetos(); } else { out.textContent = 'Não deu: ' + (b.error || r.status); }
199
+ } catch (e) { out.textContent = 'Falhou: ' + e.message; } finally { create.disabled = false; }
200
+ }
201
+ }, 'Criar projeto');
202
+ panel.replaceChildren(card, el('div', { class: 'card' }, el('label', {}, 'Novo projeto'), el('div', { class: 'row' }, nameI, create), out));
203
+ } catch (e) { if (e.message !== '401') panel.replaceChildren(el('div', { class: 'empty' }, 'Não consegui listar os projetos.')); }
204
+ }
205
+
206
+ function parseFrame(frame) {
207
+ const ev = { event: 'message', data: {} };
208
+ for (const line of frame.split(String.fromCharCode(10))) {
209
+ if (line.startsWith('event:')) ev.event = line.slice(6).trim();
210
+ else if (line.startsWith('data:')) { try { ev.data = JSON.parse(line.slice(5).trim()); } catch (_) { ev.data = { raw: line.slice(5) }; } }
211
+ }
212
+ return ev;
213
+ }
214
+
215
+ async function missao() {
216
+ let projects = [];
217
+ try { projects = (await getJson('/api/projects')).projects || []; } catch (e) { if (e.message === '401') return; }
218
+ const sel = el('select', { 'aria-label': 'projeto' });
219
+ if (!projects.length) sel.append(el('option', { value: '' }, '— nenhum projeto (crie um em Projetos) —'));
220
+ for (const p of projects) sel.append(el('option', { value: p.path }, (p.name || p.path)));
221
+ const obj = el('textarea', { placeholder: 'Descreva o que você quer. Ex.: "crie uma landing page para uma cafeteria, com seção de menu e contato".', 'aria-label': 'objetivo da missão' });
222
+ const log = el('div', { class: 'card', style: 'margin-top:16px', hidden: 'hidden' });
223
+ const run = el('button', {
224
+ class: 'act', onclick: async () => {
225
+ const projectPath = sel.value; const objective = obj.value.trim();
226
+ if (!projectPath) { alert('Escolha (ou crie) um projeto primeiro.'); return; }
227
+ if (!objective) return;
228
+ run.disabled = true; log.hidden = false; log.replaceChildren(el('p', { class: 'spin' }, 'Iniciando a missão…'));
229
+ try {
230
+ const r = await api('/api/run', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectPath, objective }) });
231
+ const reader = r.body.getReader(); const dec = new TextDecoder(); let buf = ''; log.replaceChildren();
232
+ const sep = String.fromCharCode(10) + String.fromCharCode(10);
233
+ for (;;) {
234
+ const { done, value } = await reader.read(); if (done) break;
235
+ buf += dec.decode(value, { stream: true });
236
+ let i; while ((i = buf.indexOf(sep)) >= 0) { const frame = buf.slice(0, i); buf = buf.slice(i + 2); render(parseFrame(frame)); }
237
+ }
238
+ } catch (e) { if (e.message !== '401') log.append(el('div', { class: 'ev' }, 'erro: ' + e.message)); }
239
+ finally { run.disabled = false; }
240
+ function render(ev) {
241
+ if (ev.event === 'done') { log.append(el('div', { class: 'ev' }, el('span', { class: 'tag' }, 'concluído'), 'missão finalizada.')); return; }
242
+ if (ev.event === 'error') { log.append(el('div', { class: 'ev' }, el('span', { class: 'tag' }, 'erro'), ev.data.message || '')); return; }
243
+ const d = ev.data || {}; let txt = '';
244
+ if (ev.event === 'start') txt = 'objetivo: ' + (d.objective || '');
245
+ else if (ev.event === 'result') txt = 'código de saída ' + (d.exitCode != null ? d.exitCode : '?') + (d.output ? ' — ' + clean(String(d.output)).slice(0, 400) : '');
246
+ else txt = clean(JSON.stringify(d)).slice(0, 400);
247
+ log.append(el('div', { class: 'ev' }, el('span', { class: 'tag' }, ev.event), txt));
248
+ }
249
+ }
250
+ }, 'Executar missão');
251
+ const chips = el('div', { class: 'chips' });
252
+ for (const s of ['Criar um site', 'Criar um sistema', 'Automatizar uma tarefa', 'Analisar meu projeto']) chips.append(el('button', { class: 'chip', onclick: () => { obj.value = s + ': '; obj.focus(); } }, s));
253
+ panel.replaceChildren(el('div', { class: 'card' }, el('div', { class: 'field' }, el('label', {}, 'Projeto'), sel), el('div', { class: 'field' }, el('label', {}, 'O que você quer que o time faça?'), obj), chips, el('div', { class: 'row', style: 'margin-top:14px' }, run)), log);
254
+ }
255
+
256
+ // ── navegação ────────────────────────────────────────────────────────────────
257
+ const PANELS = [
258
+ { id: 'missao', ico: '◐', title: 'Missão', sub: 'Peça o que quiser — o time de agentes executa de verdade.', run: missao },
259
+ { id: 'projetos', ico: '▤', title: 'Projetos', sub: 'Os projetos que o Nexus conhece nesta máquina.', run: projetos },
260
+ { id: 'equipe', ico: '◇', title: 'Equipe', sub: 'Os agentes e o que cada um faz.', run: () => textPanel('/api/team', 'Nenhum agente no roster.') },
261
+ { id: 'matriz', ico: '⇄', title: 'Matriz', sub: 'Quem entrega para quem (delegações).', run: () => textPanel('/api/team/matrix') },
262
+ { id: 'runbooks', ico: '☰', title: 'Runbooks', sub: 'Roteiros prontos por cenário.', run: () => textPanel('/api/runbooks', 'Nenhum runbook instalado.') },
263
+ { id: 'manual', ico: '❏', title: 'Manual', sub: 'O guia do projeto.', run: () => textPanel('/api/manual', 'Sem manual neste projeto.') },
264
+ { id: 'metricas', ico: '▧', title: 'Métricas', sub: 'Custo e qualidade reais das missões.', run: () => textPanel('/api/metrics', 'Ainda sem dados — rode uma missão.') },
265
+ { id: 'saude', ico: '♥', title: 'Saúde', sub: 'O ambiente está pronto para trabalhar?', run: saude },
266
+ ];
267
+ const nav = $('nav');
268
+ function go(p) {
269
+ $('ptitle').textContent = p.title; $('psub').textContent = p.sub;
270
+ for (const b of nav.children) b.setAttribute('aria-current', b.dataset.id === p.id ? 'true' : 'false');
271
+ location.hash = p.id; p.run();
272
+ }
273
+ for (const p of PANELS) nav.append(el('button', { 'data-id': p.id, onclick: () => go(p) }, el('i', { class: 'ico' }, p.ico), p.title));
274
+ const start = PANELS.find((p) => p.id === location.hash.slice(1)) || PANELS[0];
275
+ if (!token) $('authwarn').hidden = false;
276
+ go(start);
277
+ </script>
278
+ </body>
279
+ </html>