@mir-code/specs-platform 1.0.1

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 (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +460 -0
  3. package/assets/specs-usage-statusline.sh +77 -0
  4. package/assets/template/.agents/README.md +123 -0
  5. package/assets/template/.agents/RUNTIME.md +64 -0
  6. package/assets/template/.agents/agents/discovery-agent.md +15 -0
  7. package/assets/template/.agents/agents/drawing-agent.md +15 -0
  8. package/assets/template/.agents/agents/feature-runner.md +15 -0
  9. package/assets/template/.agents/agents/refinement-runner.md +15 -0
  10. package/assets/template/.agents/agents/refinement.md +15 -0
  11. package/assets/template/.agents/project.md +158 -0
  12. package/assets/template/.agents/scripts/check-commit.mjs +70 -0
  13. package/assets/template/.agents/scripts/validate-spec.mjs +345 -0
  14. package/assets/template/.agents/skills/discovery-agent/SKILL.md +80 -0
  15. package/assets/template/.agents/skills/discovery-writing/SKILL.md +76 -0
  16. package/assets/template/.agents/skills/drawing-agent/SKILL.md +85 -0
  17. package/assets/template/.agents/skills/drawing-writing/SKILL.md +100 -0
  18. package/assets/template/.agents/skills/execution-protocol/SKILL.md +110 -0
  19. package/assets/template/.agents/skills/execution-protocol/references/git.md +104 -0
  20. package/assets/template/.agents/skills/execution-protocol/references/relatorio-halt.md +103 -0
  21. package/assets/template/.agents/skills/feature-runner/SKILL.md +81 -0
  22. package/assets/template/.agents/skills/feature-runner/references/divergencias.md +38 -0
  23. package/assets/template/.agents/skills/feature-runner/references/implementar.md +80 -0
  24. package/assets/template/.agents/skills/feature-runner/references/localizar.md +46 -0
  25. package/assets/template/.agents/skills/mermaid-diagramming/SKILL.md +198 -0
  26. package/assets/template/.agents/skills/prototype-check/SKILL.md +90 -0
  27. package/assets/template/.agents/skills/refinement/SKILL.md +136 -0
  28. package/assets/template/.agents/skills/refinement/references/estrutura.md +27 -0
  29. package/assets/template/.agents/skills/refinement/references/mapear-terreno.md +46 -0
  30. package/assets/template/.agents/skills/refinement/references/validar-renderizacao.md +29 -0
  31. package/assets/template/.agents/skills/refinement-runner/SKILL.md +145 -0
  32. package/assets/template/.agents/skills/requirements-closure/SKILL.md +236 -0
  33. package/assets/template/.agents/skills/spec-writing/SKILL.md +258 -0
  34. package/assets/template/.agents/skills/test-strategy/SKILL.md +176 -0
  35. package/assets/template/.agents/skills/verification/SKILL.md +143 -0
  36. package/assets/template/.specs/config.json +39 -0
  37. package/assets/template/.specs/discoveries/meta.json +4 -0
  38. package/assets/template/.specs/drawings/meta.json +4 -0
  39. package/assets/template/.specs/specs/_templates/task-backend.md +75 -0
  40. package/assets/template/.specs/specs/_templates/task-frontend.md +89 -0
  41. package/assets/template/.specs/specs/_templates/task-integracao.md +72 -0
  42. package/assets/template/.specs/specs/exemplo/feat-exemplo-primeira-task.md +82 -0
  43. package/assets/template/.specs/specs/exemplo/meta.json +8 -0
  44. package/assets/template/.specs/specs/exemplo/overview.md +45 -0
  45. package/assets/template/.specs/specs/meta.json +4 -0
  46. package/assets/template/SPECS.md +213 -0
  47. package/dist/chunk-SOOETS3N.js +3490 -0
  48. package/dist/chunk-SOOETS3N.js.map +1 -0
  49. package/dist/cli.js +15 -0
  50. package/dist/cli.js.map +1 -0
  51. package/dist/index.d.ts +69 -0
  52. package/dist/index.js +16 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/ui/assets/abnfDiagram-VCTEODGH-DYXtkHNq.js +2 -0
  55. package/dist/ui/assets/abnfDiagram-VCTEODGH-DYXtkHNq.js.map +1 -0
  56. package/dist/ui/assets/arc-CJs0gz4E.js +2 -0
  57. package/dist/ui/assets/arc-CJs0gz4E.js.map +1 -0
  58. package/dist/ui/assets/architectureDiagram-5GKGNRK7-Br7ZNRfe.js +37 -0
  59. package/dist/ui/assets/architectureDiagram-5GKGNRK7-Br7ZNRfe.js.map +1 -0
  60. package/dist/ui/assets/blockDiagram-I7D4REHJ-172V7R5o.js +130 -0
  61. package/dist/ui/assets/blockDiagram-I7D4REHJ-172V7R5o.js.map +1 -0
  62. package/dist/ui/assets/c4Diagram-7LVT6UL2-B4AYFhRs.js +39 -0
  63. package/dist/ui/assets/c4Diagram-7LVT6UL2-B4AYFhRs.js.map +1 -0
  64. package/dist/ui/assets/channel-BfWO3B41.js +2 -0
  65. package/dist/ui/assets/channel-BfWO3B41.js.map +1 -0
  66. package/dist/ui/assets/chunk-2Q5K7J3B-B-u7gtZN.js +2 -0
  67. package/dist/ui/assets/chunk-2Q5K7J3B-B-u7gtZN.js.map +1 -0
  68. package/dist/ui/assets/chunk-5VM5RSS4-CjFDq_cq.js +16 -0
  69. package/dist/ui/assets/chunk-5VM5RSS4-CjFDq_cq.js.map +1 -0
  70. package/dist/ui/assets/chunk-F27PBJKO-BDYeB7B8.js +2 -0
  71. package/dist/ui/assets/chunk-F27PBJKO-BDYeB7B8.js.map +1 -0
  72. package/dist/ui/assets/chunk-IMKFNOWR-y2kXPnac.js +232 -0
  73. package/dist/ui/assets/chunk-IMKFNOWR-y2kXPnac.js.map +1 -0
  74. package/dist/ui/assets/chunk-JWPE2WC7-DyCryvDN.js +2 -0
  75. package/dist/ui/assets/chunk-JWPE2WC7-DyCryvDN.js.map +1 -0
  76. package/dist/ui/assets/chunk-POPQ4Y6H-DeaxKHvR.js +2 -0
  77. package/dist/ui/assets/chunk-POPQ4Y6H-DeaxKHvR.js.map +1 -0
  78. package/dist/ui/assets/chunk-SVP7TREG-CmcZEncL.js +89 -0
  79. package/dist/ui/assets/chunk-SVP7TREG-CmcZEncL.js.map +1 -0
  80. package/dist/ui/assets/chunk-TICWLB2K-Df1RRXWR.js +207 -0
  81. package/dist/ui/assets/chunk-TICWLB2K-Df1RRXWR.js.map +1 -0
  82. package/dist/ui/assets/chunk-XXDRQBXY-ClTat8Q0.js +2 -0
  83. package/dist/ui/assets/chunk-XXDRQBXY-ClTat8Q0.js.map +1 -0
  84. package/dist/ui/assets/classDiagram-ZZMXUADV-YTUiu8rF.js +2 -0
  85. package/dist/ui/assets/classDiagram-ZZMXUADV-YTUiu8rF.js.map +1 -0
  86. package/dist/ui/assets/classDiagram-v2-VYDZK3BY-YTUiu8rF.js +2 -0
  87. package/dist/ui/assets/classDiagram-v2-VYDZK3BY-YTUiu8rF.js.map +1 -0
  88. package/dist/ui/assets/cose-bilkent-JH36ORCC-QvqQLoy5.js +2 -0
  89. package/dist/ui/assets/cose-bilkent-JH36ORCC-QvqQLoy5.js.map +1 -0
  90. package/dist/ui/assets/cynefin-OW5HDTMX-BY6w3pAQ.js +179 -0
  91. package/dist/ui/assets/cynefin-OW5HDTMX-BY6w3pAQ.js.map +1 -0
  92. package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-luPP4ha1.js +63 -0
  93. package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-luPP4ha1.js.map +1 -0
  94. package/dist/ui/assets/cytoscape.esm-Ix0LnXOy.js +332 -0
  95. package/dist/ui/assets/cytoscape.esm-Ix0LnXOy.js.map +1 -0
  96. package/dist/ui/assets/dagre-GXQ25YYZ-DhpPwKqq.js +5 -0
  97. package/dist/ui/assets/dagre-GXQ25YYZ-DhpPwKqq.js.map +1 -0
  98. package/dist/ui/assets/defaultLocale-DX6XiGOO.js +2 -0
  99. package/dist/ui/assets/defaultLocale-DX6XiGOO.js.map +1 -0
  100. package/dist/ui/assets/diagram-S7CK7UJ4-B9wvQM0w.js +31 -0
  101. package/dist/ui/assets/diagram-S7CK7UJ4-B9wvQM0w.js.map +1 -0
  102. package/dist/ui/assets/diagram-UQ7AKVKN-gm1M_iZS.js +42 -0
  103. package/dist/ui/assets/diagram-UQ7AKVKN-gm1M_iZS.js.map +1 -0
  104. package/dist/ui/assets/diagram-VSXAHHWV-DkEB6nEx.js +4 -0
  105. package/dist/ui/assets/diagram-VSXAHHWV-DkEB6nEx.js.map +1 -0
  106. package/dist/ui/assets/diagram-VX7I27RA-Ce2F1bHi.js +25 -0
  107. package/dist/ui/assets/diagram-VX7I27RA-Ce2F1bHi.js.map +1 -0
  108. package/dist/ui/assets/diagram-Z3DM3KII-B5ArCh-E.js +25 -0
  109. package/dist/ui/assets/diagram-Z3DM3KII-B5ArCh-E.js.map +1 -0
  110. package/dist/ui/assets/ebnfDiagram-PWID7BFC-DgBM80MD.js +2 -0
  111. package/dist/ui/assets/ebnfDiagram-PWID7BFC-DgBM80MD.js.map +1 -0
  112. package/dist/ui/assets/erDiagram-RLTQ6QDP-C161af8G.js +100 -0
  113. package/dist/ui/assets/erDiagram-RLTQ6QDP-C161af8G.js.map +1 -0
  114. package/dist/ui/assets/flowDiagram-HODETNUW-B8N_WyW5.js +169 -0
  115. package/dist/ui/assets/flowDiagram-HODETNUW-B8N_WyW5.js.map +1 -0
  116. package/dist/ui/assets/ganttDiagram-EL5Y4UJY-CD2RJpTb.js +293 -0
  117. package/dist/ui/assets/ganttDiagram-EL5Y4UJY-CD2RJpTb.js.map +1 -0
  118. package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-BrDAzeFT.js +107 -0
  119. package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-BrDAzeFT.js.map +1 -0
  120. package/dist/ui/assets/index-CVWRdirI.css +32 -0
  121. package/dist/ui/assets/index-DoR97Zqf.js +408 -0
  122. package/dist/ui/assets/index-DoR97Zqf.js.map +1 -0
  123. package/dist/ui/assets/infoDiagram-27XIBGKW-HYGaMzrA.js +3 -0
  124. package/dist/ui/assets/infoDiagram-27XIBGKW-HYGaMzrA.js.map +1 -0
  125. package/dist/ui/assets/init-Gi6I4Gst.js +2 -0
  126. package/dist/ui/assets/init-Gi6I4Gst.js.map +1 -0
  127. package/dist/ui/assets/ishikawaDiagram-5VMMS53U-DXTLlBiw.js +71 -0
  128. package/dist/ui/assets/ishikawaDiagram-5VMMS53U-DXTLlBiw.js.map +1 -0
  129. package/dist/ui/assets/journeyDiagram-3NMN7TZE-DSJ8QN1S.js +140 -0
  130. package/dist/ui/assets/journeyDiagram-3NMN7TZE-DSJ8QN1S.js.map +1 -0
  131. package/dist/ui/assets/kanban-definition-UXKFOSKX-Bm5iz0k3.js +90 -0
  132. package/dist/ui/assets/kanban-definition-UXKFOSKX-Bm5iz0k3.js.map +1 -0
  133. package/dist/ui/assets/katex-C5jXJg4s.js +258 -0
  134. package/dist/ui/assets/katex-C5jXJg4s.js.map +1 -0
  135. package/dist/ui/assets/layout-DfJgW6eG.js +2 -0
  136. package/dist/ui/assets/layout-DfJgW6eG.js.map +1 -0
  137. package/dist/ui/assets/linear-B_kyVotV.js +2 -0
  138. package/dist/ui/assets/linear-B_kyVotV.js.map +1 -0
  139. package/dist/ui/assets/mermaid-block-CUJSBQFx.js +2 -0
  140. package/dist/ui/assets/mermaid-block-CUJSBQFx.js.map +1 -0
  141. package/dist/ui/assets/mermaid.core-ExHG1WnM.js +313 -0
  142. package/dist/ui/assets/mermaid.core-ExHG1WnM.js.map +1 -0
  143. package/dist/ui/assets/mindmap-definition-YA3MSWOX-B1Hh9dQz.js +97 -0
  144. package/dist/ui/assets/mindmap-definition-YA3MSWOX-B1Hh9dQz.js.map +1 -0
  145. package/dist/ui/assets/ordinal-Cboi1Yqb.js +2 -0
  146. package/dist/ui/assets/ordinal-Cboi1Yqb.js.map +1 -0
  147. package/dist/ui/assets/pegDiagram-XKGWAZYB-x242yOd7.js +2 -0
  148. package/dist/ui/assets/pegDiagram-XKGWAZYB-x242yOd7.js.map +1 -0
  149. package/dist/ui/assets/pieDiagram-E7YTZNPT-Dl-YP7Xs.js +40 -0
  150. package/dist/ui/assets/pieDiagram-E7YTZNPT-Dl-YP7Xs.js.map +1 -0
  151. package/dist/ui/assets/quadrantDiagram-AXDQQJYC-CMJ920V9.js +8 -0
  152. package/dist/ui/assets/quadrantDiagram-AXDQQJYC-CMJ920V9.js.map +1 -0
  153. package/dist/ui/assets/railroadDiagram-O6MQD6OU-1kV83y5t.js +2 -0
  154. package/dist/ui/assets/railroadDiagram-O6MQD6OU-1kV83y5t.js.map +1 -0
  155. package/dist/ui/assets/requirementDiagram-BXWQKSXE-BFd8ZZD3.js +85 -0
  156. package/dist/ui/assets/requirementDiagram-BXWQKSXE-BFd8ZZD3.js.map +1 -0
  157. package/dist/ui/assets/sankeyDiagram-P5KCCOFB-C0LZevnf.js +41 -0
  158. package/dist/ui/assets/sankeyDiagram-P5KCCOFB-C0LZevnf.js.map +1 -0
  159. package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-BasIXLJ0.js +163 -0
  160. package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-BasIXLJ0.js.map +1 -0
  161. package/dist/ui/assets/sizeCapture-INFHLROL-qAtnqB7N.js +2 -0
  162. package/dist/ui/assets/sizeCapture-INFHLROL-qAtnqB7N.js.map +1 -0
  163. package/dist/ui/assets/stateDiagram-D77RDMKH-CllWDWZP.js +2 -0
  164. package/dist/ui/assets/stateDiagram-D77RDMKH-CllWDWZP.js.map +1 -0
  165. package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-Dwntf8Tk.js +2 -0
  166. package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-Dwntf8Tk.js.map +1 -0
  167. package/dist/ui/assets/swimlanes-42K2YHIH-Bgt0KslH.js +2 -0
  168. package/dist/ui/assets/swimlanes-42K2YHIH-Bgt0KslH.js.map +1 -0
  169. package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-C9abicdw.js +9 -0
  170. package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-C9abicdw.js.map +1 -0
  171. package/dist/ui/assets/timeline-definition-24CTP7MA-BYO2h9Qr.js +121 -0
  172. package/dist/ui/assets/timeline-definition-24CTP7MA-BYO2h9Qr.js.map +1 -0
  173. package/dist/ui/assets/vennDiagram-4TSXK5OY-ZzyiwIUi.js +35 -0
  174. package/dist/ui/assets/vennDiagram-4TSXK5OY-ZzyiwIUi.js.map +1 -0
  175. package/dist/ui/assets/wardleyDiagram-VM6X3IG4-DfjKCK0N.js +79 -0
  176. package/dist/ui/assets/wardleyDiagram-VM6X3IG4-DfjKCK0N.js.map +1 -0
  177. package/dist/ui/assets/xychartDiagram-S5SC5T6Z-Z7daOfmM.js +8 -0
  178. package/dist/ui/assets/xychartDiagram-S5SC5T6Z-Z7daOfmM.js.map +1 -0
  179. package/dist/ui/favicon.svg +6 -0
  180. package/dist/ui/index.html +21 -0
  181. package/package.json +70 -0
  182. package/scripts/copy-ui.mjs +21 -0
  183. package/scripts/fix-node-pty-perms.cjs +73 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Samir El Hassan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,460 @@
1
+ # @mir-code/specs-platform
2
+
3
+ Plataforma agnóstica de documentação de specs — lê `.specs/specs/` de qualquer projeto (Node, Java, Python, Go, etc.), renderiza em dark mode com drag-and-drop, status por modal e disparo de agents (Claude Code / Cursor) por um clique.
4
+
5
+ Faz parte do monorepo [`mircode-ai-tools`](../../README.md). Pode ser usado sozinho (comando `specs`) ou pelo CLI guarda-chuva [`@mir-code/ai-tools`](../ai-tools/README.md) (comando `mircode-ai specs-platform ...` ou menu interativo `mircode-ai`).
6
+
7
+ ## Instalação (uma vez por máquina)
8
+
9
+ ```bash
10
+ npm i -g @mir-code/specs-platform
11
+ ```
12
+
13
+ Ou sem instalar: `npx @mir-code/specs-platform <comando>`.
14
+
15
+ O pacote traz o servidor e a UI já buildados. Nada de código da plataforma é copiado para o seu projeto e o `package.json` dele (se existir) não é tocado.
16
+
17
+ ## Uso
18
+
19
+ ### Primeira vez num projeto
20
+
21
+ Na raiz do projeto (qualquer stack):
22
+
23
+ ```bash
24
+ specs install # .agents/ + symlinks, .specs/, SPECS.md, .gitignore
25
+ specs start # sobe UI + API em background (:4321) e abre o browser
26
+ ```
27
+
28
+ O `specs install` grava no seu projeto:
29
+
30
+ - `.agents/` — **fonte única** de agentes e skills: 5 launchers (`feature-runner`, `refinement`, `refinement-runner`, `discovery-agent`, `drawing-agent`), 14 skills, 2 scripts de gate e o **`project.md`** — o formulário onde você declara a stack, os comandos de teste e as ferramentas do seu projeto. As skills são agnósticas: **só o `project.md` precisa ser preenchido**.
31
+ - `.claude/{agents,skills}` e `.cursor/{agents,skills}` — symlinks para `.agents/`, para Claude Code e Cursor lerem os mesmos arquivos. Se já existirem como diretórios reais, são movidos para `.bak-<timestamp>` antes.
32
+ - `.specs/config.json` — config da plataforma (porta, agente, terminal, `featuresDir`, `discoveriesDir`, `drawingsDir`).
33
+ - `.specs/specs/meta.json` + `_templates/` + a feature `exemplo/` — scaffold de docs (só se não existir ainda).
34
+ - `.specs/discoveries/meta.json` e `.specs/drawings/meta.json` — scaffolds de discoveries (RFCs, spikes, ADRs) e desenhos (Mermaid).
35
+ - `SPECS.md` — guia de operação e troubleshooting para quem abrir o projeto.
36
+ - `.gitignore` — entradas para o estado local (`.specs/.jobs.json`, `.specs/.run/`).
37
+
38
+ ### Comandos
39
+
40
+ | Comando | O que faz |
41
+ |---|---|
42
+ | `specs install [dir]` (alias `update`) | Instala/atualiza. Idempotente: regrava agents/skills, preserva o que é do projeto. |
43
+ | `specs install --force` | Sobrescreve também `config.json`, `project.md`, `SPECS.md` e scaffolds. |
44
+ | `specs install --clean-legacy` | Remove o `.specs/app/` de instalações antigas. |
45
+ | `specs start` | Sobe UI + API numa porta só, em background (pid/log em `.specs/.run/`). |
46
+ | `specs start -f` | Foreground (Ctrl+C para parar). Flags: `-p <porta>`, `--no-open`, `--api-only`. |
47
+ | `specs status` / `specs stop` | Estado / parada da instância em background do projeto atual. |
48
+ | `specs statusline` | Imprime o trecho do `~/.claude/settings.json` para o medidor de uso do plano. |
49
+
50
+ ### Atualizar
51
+
52
+ ```bash
53
+ npm i -g @mir-code/specs-platform@latest # nova versão da UI/server
54
+ specs install # propaga agents/skills novos para o projeto
55
+ ```
56
+
57
+ ### Migrando da versão antiga (`.specs/app/` + abbrs fish)
58
+
59
+ A versão anterior copiava o monorepo inteiro para `.specs/app/` e era operada por `specs:apply` / `specs:run` / `specs:stop`. Agora:
60
+
61
+ | Antes | Agora |
62
+ |---|---|
63
+ | `specs:apply` | `specs install` |
64
+ | `specs:run` | `specs start` |
65
+ | `specs:stop` | `specs stop` |
66
+ | `specs:logs` | `tail -f .specs/.run/specs.log` |
67
+ | Fastify `:4321` + Vite `:5173` | UI + API em `:4321` |
68
+
69
+ Rode `specs install --clean-legacy` em cada projeto e remova as abbrs `specs:*` do `config.fish`.
70
+
71
+ ## Navegação — sidebar colapsável (shadcn/ui)
72
+
73
+ A marca da plataforma é o caret de um prompt (`›`) seguido de duas barras — a spec sendo escrita
74
+ por um agent. Ela vive em `internal/specs-ui/src/components/shell/specs-logo.tsx` (glifo nu, usado no
75
+ header e no rail da sidebar) e em `internal/specs-ui/public/favicon.svg` (mesmo glifo dentro de um
76
+ quadrado roxo, que é o que dá presença na aba do browser — o glifo nu some em abas claras).
77
+
78
+ A navegação lateral usa o componente `Sidebar` do **shadcn/ui**
79
+ (`internal/specs-ui/src/components/ui/sidebar.tsx`). Recolhe e expande por **⌘B / Ctrl+B** ou pelo botão
80
+ no topo dela; quando recolhida vira um **rail de 44px** com ícone, contador `em-andamento/total` e
81
+ o rótulo "SPECS" na vertical — clicar no rail reabre. O estado é persistido no cookie
82
+ `sidebar_state`.
83
+
84
+ Expandida, ela é dividida em: **header** (título + contador + "nova spec" + botão de recolher +
85
+ filtro "ocultar concluídas") e **content** (specs com tasks, discoveries, desenhos e protótipo —
86
+ todos com drag & drop).
87
+
88
+ Do outro lado da tela, na borda **direita**, fica a **sidebar de Execuções** (descrita abaixo) —
89
+ um segundo painel `Sidebar` que colapsa de forma independente. O layout é, então:
90
+ `[ Specs ][ conteúdo + índice da página ][ Execuções ]`.
91
+
92
+ Os tokens do shadcn/ui (`--sidebar*`, `--color-background`, `--color-primary`, …) são declarados em
93
+ `internal/specs-ui/src/styles/global.css` **mapeados na paleta dark existente** — a variável `--accent`
94
+ (roxo) continua sendo a cor de marca e a superfície de hover mora em `--surface-hover`.
95
+
96
+ ## Atualização automática (sem F5)
97
+
98
+ O servidor observa `.specs/` com `fs.watch` recursivo e empurra as invalidações para a UI por SSE
99
+ em `GET /api/events`. Editar um `.md` por fora — um agent rodando, seu editor, um `git checkout` —
100
+ reflete na tela em **menos de meio segundo**, sem recarregar: o corpo da task, o título e o status
101
+ na sidebar, as discoveries, os desenhos e o `config.json`.
102
+
103
+ Cada caminho vira só as invalidações que ele implica (`internal/specs-server/src/specs-watcher.ts`): um
104
+ `.md` de task invalida aquele conteúdo **e** a árvore (o frontmatter carrega título e status); um
105
+ `meta.json`, só a árvore; o `config.json` arrasta o protótipo junto. Escritas em rajada — que é como
106
+ um editor salva — são agrupadas num lote deduplicado com 150 ms de debounce.
107
+
108
+ Cadências, depois dessa mudança:
109
+
110
+ | dado | como atualiza |
111
+ | --- | --- |
112
+ | árvore, conteúdo de task, discoveries, desenhos, config | push por SSE, sem polling |
113
+ | uso do plano | push por SSE (os arquivos de origem também são observados, com watch que se re-arma) + rede de segurança de 5 min |
114
+ | execuções (jobs) | polling adaptativo: 3 s com job ativo, 20 s com tudo terminado |
115
+
116
+ Jobs continuam em polling porque o estado ao vivo vive em memória no servidor, não no disco — não há arquivo para
117
+ observar. O que mudou é a cadência: antes eram 3 s para sempre.
118
+
119
+ Arquivos avulsos (os de uso do Claude) usam `watchFileRearming`: `fs.watch` prende o inode, e um
120
+ save atômico — gravar num `.tmp` e renomear por cima — o troca, matando o watch depois do primeiro
121
+ save. O watch é re-armado a cada evento, e retentado a cada 5 s enquanto o arquivo não existir.
122
+
123
+ Se o SSE cair, ou o SO não suportar watch recursivo (o `hello` do stream traz `watching: false`), a
124
+ UI volta sozinha a pollar a cada 10 s e reconecta com backoff — nunca fica desatualizada em
125
+ silêncio.
126
+
127
+ > Com revalidação ao vivo, os diálogos de edição semeiam o editor **uma vez** por abertura. Sem
128
+ > isso, um refetch no meio da digitação sobrescreveria o que ainda não foi salvo.
129
+
130
+ ## Executions — terminal inline com a sidebar de Execuções
131
+
132
+ Toda execução disparada pela UI ("Rodar Tarefa", "Nova spec", "Nova discovery", "Novo desenho", "Editar via IA") passa por um gerenciador de jobs que roda o agent dentro do próprio servidor Fastify via PTY (`node-pty`), e streama a saída para um terminal xterm.js embutido na UI.
133
+
134
+ **Cada execução é etiquetada pela origem** — `Spec`, `Refino`, `Discovery`, `Desenho`, `Protótipo` — num badge colorido na sidebar e no cabeçalho do terminal, então dá para achar a execução certa sem ler o label inteiro.
135
+
136
+ **Mudança de estado não vira popup.** Quando um job passa a esperar input ou termina, a plataforma toca um som e marca o título da aba (`(!) Specs — 1 esperando`); quem mostra o quê é a sidebar de Execuções, com a linha realçada em âmbar e o trecho do terminal que fez a pergunta. Toast e Notification do browser foram removidos: eles despejavam o buffer cru do PTY numa caixa que o usuário não pediu. O ponto de status também distingue pela **forma**, não só pela cor — cheio e pulsando enquanto há processo vivo, anel vazado e estático quando acabou.
137
+
138
+ **As execuções sobrevivem ao restart.** O registro de cada job (label, tipo, tempos, status e o fim do buffer) é gravado em `.specs/.jobs.json`; ao subir, o servidor repõe a lista. Os processos não voltam — eram filhos do servidor anterior — então quem estava vivo reaparece como `Cancelado · sessão anterior`, legível mas sem input. O arquivo é estado local da máquina e entra no `.gitignore` pelo `specs install`.
139
+
140
+ - **Modal "Rodar Tarefa"** tem quatro seções: **Escopo** (feature / task / feature sem pausar), **Ferramenta** (**Claude Code** ou **Cursor**), **Modelo** e **Esforço**. As escolhas são persistidas em `localStorage`. A execução é sempre **inline** (terminal dentro da UI) — não há mais seletor "Onde rodar". O default de CLI fica em `agent.cli` (default `claude`) no `.specs/config.json`.
141
+ - **CLI selecionável (Claude Code / Cursor)** — todos os disparos (Rodar Tarefa, Nova spec, Nova discovery, Editar via IA) mostram o seletor **Ferramenta** quando há mais de uma CLI em `agent.commands`. O Cursor (`cursor-agent`) lê `.claude/agents/` por compatibilidade, então `feature-runner`/`refinement`/`discovery-agent` rodam nas duas CLIs sem duplicar arquivos.
142
+ - **Modelo selecionável (listagem automática)** — o seletor **Modelo** é populado por `GET /api/agent-models?cli=<cli>`. Para o Cursor, a lista é **dinâmica** via `cursor-agent --list-models`; para o Claude Code (que não expõe listagem por CLI), usa-se a lista estática de aliases em `agent.models`. O modelo escolhido vira `--model <id>` via placeholder `{model}` no comando. A escolha é lembrada por CLI no `localStorage`.
143
+ - **Dialog do terminal** abre ao criar um job inline — xterm completo com input, botão **Minimizar** (mantém rodando em background) e **Parar** (SIGINT → SIGTERM → SIGKILL).
144
+ - **Sidebar de Execuções** — painel lateral próprio (288px) na borda **direita** da tela, colapsável de forma independente (vira um rail de 44px com ícone, contador e rótulo vertical; estado lembrado em `localStorage`). Lista os jobs **ordenados por data de execução, mais recente primeiro** — com data relativa ("agora", "há 12 min", "hoje 14:03", "ontem 23:40", "04/07 08:00"), tipo, status e duração. Clicar reabre o terminal; o botão de ação no hover para (execução ativa) ou remove do histórico (execução finalizada). O contador no header mostra `ativas/total`.
145
+ - **Uso do plano** no rodapé da sidebar de Execuções: barra preenchida da janela de **sessão (5h)** e da **semanal (7d)**, com percentual usado, quanto resta e quanto falta para resetar. A fonte é local — o histórico que o app desktop do Claude grava em `~/Library/Application Support/Claude/plan-usage-history.json` a cada ~15–25 min enquanto está aberto, com o `~/.claude/rate-limits-cache.json` do CLI como fallback quando for mais recente. O horário do reset **não** está nesses arquivos, então é deduzido das quedas de percentual do histórico (marcado com `~`): a janela de 5h ancora na hora cheia dentro do intervalo da última queda; a semanal, que tem fase fixa, é achada testando cada hora do período e pesando cada queda pela estreiteza do seu intervalo. O carimbo "há N min" ao lado do título mostra a idade da medida. Passando de **45 min** (bem acima do intervalo normal de gravação, ~15–25 min), o bloco inteiro esmaece, ganha um ícone de alerta e troca o rodapé por "medida congelada" — um número velho aqui é pior que número nenhum, porque a janela de sessão pode ter virado sem a gente ver. E quando a janela de 5h está zerada com dado fresco, o rodapé diz "janela parada · começa na próxima mensagem", que é o estado real: ela só volta a correr quando você manda a próxima mensagem. Sem nenhuma das duas fontes, o bloco simplesmente não aparece.
146
+ - **Notificações** quando um job muda de estado: contador colorido na sidebar de Execuções, `document.title` com `(!) Specs — N esperando`, toast (Sonner), notificação do SO (com permissão) e beep sutil via WebAudio. Som pode ser desabilitado em `agent.sound: false`.
147
+ - **Detecção de input pendente** — o server analisa o buffer do PTY após cada chunk e marca `needs-input` quando detecta padrões do Claude Code (`?`, `(y/N)`, `Do you want to`, `❯`) ou inatividade > 2.5s. Padrões customizáveis em `agent.inputPromptPatterns`.
148
+
149
+ Campos novos no `.specs/config.json` (todos com defaults retrocompatíveis):
150
+
151
+ ```json
152
+ {
153
+ "agent": {
154
+ "cli": "claude",
155
+ "commands": {
156
+ "claude": "claude {model} {effort} --agent feature-runner \"{prompt}\"",
157
+ "cursor": "cursor-agent {model} \"Use the feature-runner subagent. {prompt}\""
158
+ },
159
+ "modelsCommand": { "cursor": "cursor-agent --list-models" },
160
+ "models": { "claude": ["opus", "sonnet", "haiku", "fable"] },
161
+ "defaultExecutionMode": "inline",
162
+ "sound": true,
163
+ "inputPromptPatterns": ["\\?\\s*$", "\\((?:y\\/n|Y\\/n|y\\/N|Y\\/N)\\)", "Do you want to", "❯\\s*$"],
164
+ "bufferBytesCap": 2000000
165
+ }
166
+ }
167
+ ```
168
+
169
+ Endpoints adicionados (todos em `http://localhost:4321`):
170
+
171
+ | Método | Rota | Função |
172
+ |---|---|---|
173
+ | `POST` | `/api/jobs` | Cria job (inline ou external). Body: `{ kind, mode, cli?, model?, effort?, ...payload }`. |
174
+ | `GET` | `/api/jobs` | Lista jobs ativos/finalizados. |
175
+ | `GET` | `/api/agent-models?cli=<cli>` | Lista modelos da CLI (`{ models: {id,label}[], source }`). |
176
+ | `GET` | `/api/jobs/:id` | Snapshot + buffer completo. |
177
+ | `GET` | `/api/jobs/:id/stream` | SSE — chunks + mudanças de status. |
178
+ | `POST` | `/api/jobs/:id/input` | Escreve no PTY (keystrokes). |
179
+ | `POST` | `/api/jobs/:id/resize` | Resize do PTY. |
180
+ | `POST` | `/api/jobs/:id/stop` | SIGINT → SIGTERM → SIGKILL. |
181
+ | `DELETE` | `/api/jobs/:id` | Remove job terminado do histórico. |
182
+
183
+ As rotas antigas `/api/run-agent`, `/api/run-refinement`, `/api/run-discovery-agent` continuam funcionais (modo `external` agora vai pela mesma UI via `/api/jobs`, mas os clientes existentes seguem compatíveis).
184
+
185
+ ## Requisitos
186
+
187
+ - Node.js >= 20 (o seu projeto não precisa ser Node)
188
+ - macOS ou Linux (Windows não testado). O PTY usa `node-pty` com prebuilts para `darwin-arm64`, `darwin-x64`, `linux-arm64`, `linux-x64`.
189
+
190
+ ## Onde está o código
191
+
192
+ | Parte | Caminho no monorepo |
193
+ |---|---|
194
+ | CLI `specs` (install/start/stop) | `packages/specs-platform/src/` |
195
+ | Template copiado para o projeto | `packages/specs-platform/assets/template/` |
196
+ | Servidor Fastify (interno, embutido no bundle) | `internal/specs-server/` |
197
+ | UI Vite + React (interna, build em `dist/ui`) | `internal/specs-ui/` |
198
+
199
+ ## Escolha de modelo e esforço
200
+
201
+ O modal **Rodar Tarefa** (e os diálogos de refinement/discovery) trazem dois seletores do design
202
+ system, ordenados sempre do mais leve ao mais capaz:
203
+
204
+ - **Modelo** — barra de potência (verde → amarelo → vermelho), custo por milhão de tokens
205
+ (entrada / saída), janela de contexto e selos de variante: **promo** (preço promocional
206
+ vigente), nível de esforço embutido no id, **thinking** e **fast**. Listas com mais de 8 itens
207
+ ganham **campo de busca** no topo (casa por nome, id e fornecedor).
208
+ - **Catálogo por família, não por id** (`internal/specs-ui/src/lib/model-catalog.ts`) — o `cursor-agent`
209
+ lista ~200 ids que são a mesma família com sufixos (`claude-opus-5-thinking-xhigh-fast`), então
210
+ o resolvedor descasca `-fast`, `-thinking` e o nível de esforço até achar a família.
211
+ - **De onde vem cada informação** — preço e janela de contexto só são declarados para os modelos
212
+ do catálogo oficial Claude. Para o resto, a janela vem do label que a própria CLI devolve
213
+ ("Opus 5 1M Thinking") e o preço simplesmente não aparece: a CLI do Cursor não publica tabela e
214
+ estimativa seria pior que omissão. No Cursor, o preço mostrado é o da API Anthropic e serve como
215
+ referência de peso relativo — o consumo real sai do plano do Cursor.
216
+ - **Esforço** — `low` · `medium` · `high` · `xhigh` · `max`, com uma linha explicando o que cada
217
+ nível troca. No Claude Code vira `--effort <nível>`; no cursor-agent, parâmetro do próprio
218
+ modelo (`modelo[effort=…]`), então lá ele exige um modelo escolhido. Quando o id do modelo já
219
+ fixa o nível (`…-max`, `…-thinking-high`), o seletor de esforço desabilita e diz qual nível está
220
+ em vigor, em vez de deixar você passar um valor conflitante.
221
+
222
+ Ambos são persistidos por CLI no navegador e reaproveitados pelo atalho `R`. A execução é sempre
223
+ **inline** (terminal dentro da UI).
224
+
225
+ ## Code review local
226
+
227
+ Toda task tem um botão **Revisar** na topbar (`/features/<slug>/<task>/review`). A tela mostra as
228
+ alterações do working tree contra o `HEAD` — staged, não staged e untracked — agrupadas em
229
+ **Código**, **Specs e documentação** e **Gerados e mecânicos** (lock, snapshot, migration SQL).
230
+
231
+ - **Tudo nasce recolhido** — a tela abre com a lista fechada e nada de diff carregado; expandir é
232
+ uma escolha (por arquivo, por commit, ou **Expandir tudo** no cabeçalho). Cada arquivo é um card
233
+ com status, contagem `+/−` e o diff colorido com numeração de linha, carregado **sob demanda**.
234
+ - **Abrir no editor** dispara o comando de `review.openCommand` no `.specs/config.json`
235
+ (default `cursor --goto {path}:{line}`; troque por `code --goto {path}:{line}`, `idea`, etc.).
236
+ - **Revisar** marca o arquivo como visto; a marcação é local e cai sozinha quando o arquivo muda
237
+ de novo, então retomar uma revisão interrompida não exige recomeçar.
238
+
239
+ Quando a execução é de uma **feature inteira**, o `feature-runner` fecha cada task em um commit
240
+ local e só faz push no fim. A tela então mostra **um bloco por commit** — um por task, com o diff
241
+ isolado — e, abaixo, o que ainda está no working tree. Sem isso, revisar uma feature de 20 tasks
242
+ seria um único diff plano de centenas de arquivos. A seção "Commits não enviados" é um bloco
243
+ colapsável e também começa fechada, para quem represa vários commits antes de dar push.
244
+
245
+ O botão **Revisar** da topbar sinaliza quando há trabalho esperando: fica destacado e mostra a
246
+ quantidade de arquivos não commitados e, com a seta `↑`, quantos commits locais ainda não foram
247
+ enviados.
248
+
249
+ Endpoints: `GET /api/review/changes`, `GET /api/review/diff?path=[&commit=]`,
250
+ `POST /api/review/open`.
251
+
252
+ ## Protótipo — Claude Design ou Pencil
253
+
254
+ Projetos com entregável visual declaram o protótipo no `.specs/config.json`. É essa declaração que
255
+ diz **qual ferramenta** o projeto usa — e tanto a Specs Platform quanto os agents partem dela antes
256
+ de qualquer verificação visual:
257
+
258
+ ```json
259
+ {
260
+ "prototype": {
261
+ "tool": "claude-design",
262
+ "title": "Finance — redesign com shadcn/ui",
263
+ "url": "https://claude.ai/code/artifact/<id>",
264
+ "designDir": "design/finance-redesign",
265
+ "artboards": ["Desktop", "Mobile", "Loading", "Erro e vazio"]
266
+ }
267
+ }
268
+ ```
269
+
270
+ | Campo | Para quê |
271
+ |---|---|
272
+ | `tool` | `claude-design` (canvas publicado, endereçado por `url`) ou `pencil` (arquivo `.pen` local, endereçado por `file`). |
273
+ | `title` | Nome exibido no painel. |
274
+ | `url` / `file` | O alvo. Um dos dois é obrigatório, conforme a ferramenta — declaração incompleta é ignorada em vez de virar botão quebrado. |
275
+ | `designDir` | Pasta com o `README.md` de decisões do protótipo (a parte versionada). |
276
+ | `artboards` | Telas listadas no painel. |
277
+ | `nodeIds` | Pencil: nós de referência, quando o projeto quiser fixá-los. |
278
+ | `openCommand` | Pencil: sobrescreve `open -a Pencil {file}`. |
279
+ | `snapshot` | HTML do protótipo salvo no repo, servido pela plataforma para o iframe. Default: `<designDir>/canvas.html`. |
280
+
281
+ O `meta.json` de uma feature pode declarar o seu próprio bloco `prototype`, que **substitui** o do
282
+ projeto — uma feature pode estar em outra ferramenta que o resto do repositório. Sem bloco algum, a
283
+ plataforma esconde o painel e os agents pulam a fase visual.
284
+
285
+ **Na Specs Platform**, a navegação lateral ganha a seção **Protótipo**, que abre a página
286
+ `/prototype` — o protótipo renderizado **dentro do app**, em iframe, sem trocar de aba. Como o canvas
287
+ publicado manda `frame-ancestors 'self'` (vale para `/artifact/<id>`, `/embed` e
288
+ `/public/artifacts/<id>`), o que vai no iframe é o **snapshot local**: o HTML do canvas salvo no
289
+ repositório e servido pelo próprio Fastify, em `GET /api/prototype/frame`. O snapshot renderiza o
290
+ canvas completo, em modo somente leitura, com todos os artboards.
291
+
292
+ Botões da página: **Sincronizar** (dispara um job que relê o canvas publicado e regrava o snapshot),
293
+ **Editar no canvas** / **Abrir no Pencil**, e **Pedir alteração**.
294
+
295
+ As páginas de feature e de task também ganham o painel **Protótipo**, com a fonte da verdade, os
296
+ artboards e os botões **Ver aqui** (leva à página com o iframe), **Editar no canvas** e **Pedir
297
+ alteração** — este abre um diálogo de texto livre e dispara um **job** como qualquer execução de
298
+ agent (terminal ao vivo, notificação ao fim). O prompt é montado pelo servidor conforme a
299
+ ferramenta: `/design ...` no Claude Design; instrução de editar pelo **Pencil MCP**, devolvendo os
300
+ node IDs afetados, no Pencil. Feature e task vão junto como contexto.
301
+
302
+ **Nos agents**, a skill `prototype-check` carrega o fluxo completo: identificar a ferramenta
303
+ primeiro, e só então aplicar as verificações dela — artboards por nome no Claude Design (não há node
304
+ ID lá), consulta e node IDs pelo Pencil MCP no Pencil (o `.pen` é encriptado; nunca `Read`/`Grep`).
305
+
306
+ Endpoints: `GET /api/prototype[?feature=]`, `GET /api/prototype/frame[?feature=]`,
307
+ `POST /api/prototype/open`, e `POST /api/jobs` com `kind: "design"` (`intent: "change" | "snapshot"`).
308
+
309
+ ## Formato das páginas
310
+
311
+ O renderer suporta GFM, tabelas, syntax highlight, Mermaid e HTML embutido. Três convenções
312
+ valem para todo conteúdo em `.specs/specs/`:
313
+
314
+ - **Título único** — o `title` do frontmatter vira o `<h1>` da página. Não repita um `# Título`
315
+ no corpo (se repetir, o renderer descarta o duplicado).
316
+ - **Blocos expansíveis** — `<details>`/`<summary>` colapsam o detalhamento técnico longo. Deixe
317
+ uma linha em branco depois do `</summary>` e antes do `</details>` para o markdown interno ser
318
+ interpretado.
319
+ - **Diagramas Mermaid** — clique no diagrama (ou no ícone de expandir) para abrir em tela cheia,
320
+ com fit automático, zoom por scroll e pan por arrasto. Convenção de cores nos templates:
321
+ `:::changed` amarelo (alterado), `:::added` verde (adicionado), `:::removed` vermelho
322
+ (removido), `:::untouched` cinza (contexto).
323
+
324
+ Cada feature pode ter um `overview.md` (visão macro + diagrama da feature inteira). Ele é a
325
+ página de `/features/<slug>` e lista as tasks com status abaixo do conteúdo; sem ele, a rota
326
+ redireciona para a primeira task.
327
+
328
+ ## Uso do plano sem depender do app desktop
329
+
330
+ O medidor "Uso do plano" no rodapé das Execuções lê a fonte local **mais recente** entre três:
331
+
332
+ | fonte | quem escreve | quando |
333
+ | --- | --- | --- |
334
+ | `statusline` | `assets/specs-usage-statusline.sh`, plugado no seu `statusLine` | a cada turno de uma sessão do Claude Code |
335
+ | `desktop-history` | app desktop do Claude (`plan-usage-history.json`) | a cada ~15–25 min, **só com o app aberto** |
336
+ | `cli-cache` | `~/.claude/rate-limits-cache.json` | fóssil: versões recentes do Claude Code não reescrevem mais |
337
+
338
+ **Não existe endpoint da API para isso.** A Usage & Cost Admin API exige chave de organização do
339
+ Console (indisponível para contas individuais) e reporta tokens/custo da API — não a janela de 5h/7d
340
+ do plano Pro/Max. O dado da janela só chega em headers de uma resposta da API. Quem o recebe e
341
+ repassa é o **Claude Code**, no JSON do statusLine (`rate_limits.five_hour` / `.seven_day`), para
342
+ assinantes Pro/Max e a partir da primeira resposta da sessão.
343
+
344
+ Daí a fonte `statusline`: um script no meio do caminho grava esse pedaço em
345
+ `~/.claude/specs-usage.json` — que o servidor observa — e repassa o mesmo JSON ao seu statusLine
346
+ original, ecoando a saída dele sem alterar nada. `specs statusline` imprime o trecho para o
347
+ `~/.claude/settings.json` com o caminho do script dentro do pacote instalado:
348
+
349
+ ```json
350
+ "statusLine": {
351
+ "type": "command",
352
+ "command": "bash <pacote>/assets/specs-usage-statusline.sh ~/.claude/statusline-command.sh"
353
+ }
354
+ ```
355
+
356
+ Sem statusLine próprio, chame sem o argumento final — o script imprime `Modelo · 5h N% · 7d N%`.
357
+ Requer `jq`. Sem `rate_limits` no payload (conta API key, ou antes da primeira resposta) o script
358
+ não toca no arquivo: um snapshot antigo e honesto vale mais que um vazio.
359
+
360
+ Com essa fonte, o reset das janelas vem **medido**, não inferido do histórico (`resetEstimated: false`).
361
+
362
+ Passando de 45 min sem medida nova o bloco esmaece; passando de 12 horas ele para de mostrar
363
+ porcentagem e diz há quanto tempo está sem medida — um número velho com cara de número atual é
364
+ pior que número nenhum.
365
+
366
+ ## Agents e skills entregues
367
+
368
+ Os **launchers** de `.agents/agents/` têm 15 linhas: frontmatter e uma frase mandando carregar a
369
+ skill de mesmo nome. Todo o comportamento vive nas skills. Para mudar o que um agente faz, edite a
370
+ skill — nunca o launcher.
371
+
372
+ ### Os 5 agentes
373
+
374
+ | Agent | Quando usar |
375
+ |---|---|
376
+ | **`refinement`** | "refine X" / "detalhe essa feature" — transforma descrição em task documentada em `.specs/specs/`. |
377
+ | **`refinement-runner`** | "refine e já executa X" — esgota as dúvidas, implementa e para no code review. Não cria documento de spec. |
378
+ | **`feature-runner`** | "execute X" / "roda a task Y" — implementa a task documentada, para antes do commit. |
379
+ | **`discovery-agent`** | "pesquise", "avalie alternativas", "faça um spike" — produz RFC/spike/ADR em `.specs/discoveries/`. |
380
+ | **`drawing-agent`** | "desenhe a arquitetura", "mapeie o fluxo" — produz diagrama Mermaid em `.specs/drawings/`. |
381
+
382
+ ### As 14 skills
383
+
384
+ **Processo** — o que fazer, em que ordem. Cada uma tem `SKILL.md` curto com princípios e mapa de
385
+ fases; o detalhe de cada fase mora em `references/`, lido só quando a fase começa.
386
+
387
+ `feature-runner` · `refinement` · `refinement-runner` · `discovery-agent` · `drawing-agent` ·
388
+ `execution-protocol` (protocolo comum aos runners: princípios invioláveis, blast radius, git,
389
+ halt → ajustar → complete).
390
+
391
+ **Conhecimento** — como fazer bem:
392
+
393
+ | Skill | Cobre |
394
+ |---|---|
395
+ | `spec-writing` | estrutura da página de spec, diagrama Mermaid com código de cores, blocos expansíveis, `overview.md`, registro de Execuções |
396
+ | `requirements-closure` | critérios de aceite em EARS, IDs de requisito, sweep das 9 dimensões implícitas, portão de fechamento |
397
+ | `test-strategy` | matriz de cobertura por camada, os três gates, co-location de testes, Test Adequacy Review (A–D) |
398
+ | `verification` | Verifier independente (autor ≠ verificador), evidence-or-zero, sensor de discriminação por injeção de falha |
399
+ | `discovery-writing` | os quatro tipos (rfc/spike/adr/note), frontmatter, estrutura por tipo |
400
+ | `drawing-writing` | os cinco tipos de desenho, o frontmatter e a regra de "só o diagrama" |
401
+ | `mermaid-diagramming` | Mermaid que renderiza de primeira neste renderer: layout, paleta escura, `classDef`, armadilhas |
402
+ | `prototype-check` | identificar a ferramenta de protótipo (Claude Design ou Pencil) e consultá-la antes de implementar |
403
+
404
+ ### A customização mora em um arquivo só
405
+
406
+ As skills são **agnósticas ao projeto**: contêm método, não valores. Não sabem sua stack, seus
407
+ caminhos de teste, sua lib de UI nem sua ferramenta de protótipo.
408
+
409
+ Isso tudo vive em **`.agents/project.md`**, que chega como formulário em branco. Enquanto um campo
410
+ estiver `<preencher>`, a skill que depende dele **para e pergunta** em vez de adivinhar; campo que
411
+ não se aplica recebe `n/a`.
412
+
413
+ | Seção de `project.md` | Consumida por |
414
+ |---|---|
415
+ | Identidade · Stack | `feature-runner`, `refinement` |
416
+ | Pacotes, testes e comandos | `test-strategy` |
417
+ | Convenções de código | `execution-protocol`, `feature-runner` |
418
+ | Design e UI · Segurança de inputs | `ui-standards`, `input-security` (skills opcionais) |
419
+ | Ferramentas visuais | `prototype-check` |
420
+ | Scratch e temporários | `verification` |
421
+
422
+ **Consequência prática:** em `specs install`, agentes e skills são **sempre atualizados** — o template
423
+ evolui e propaga. Só o `project.md` e as skills que você adicionou por conta própria são preservados.
424
+ Customize pelo `project.md`, não editando skill.
425
+
426
+ ### Skills opcionais
427
+
428
+ `ui-standards` (padrões de UI) e `input-security` (sanitização de inputs) são referenciadas pelas
429
+ skills de processo como **condicionais**: se existirem no projeto, são carregadas; se não, o agente
430
+ segue sem elas. Para adotá-las, copie de um projeto que as tenha e preencha as seções
431
+ correspondentes do `project.md`.
432
+
433
+ ### Gates determinísticos
434
+
435
+ O que é estrutural roda por código, não por memória:
436
+
437
+ ```bash
438
+ node .agents/scripts/validate-spec.mjs .specs/specs/<slug>/feat-<slug>-<task>.md
439
+ node .agents/scripts/check-commit.mjs --message "feat(login): tela de login"
440
+ ```
441
+
442
+ Saída ≠ 0 significa **pare e corrija**.
443
+
444
+ Operação da plataforma (subir, parar, atualizar, diagnosticar) é feita pelo CLI `specs` +
445
+ troubleshooting em `SPECS.md`. O antigo agent `specs-runner` foi descontinuado —
446
+ `specs install` remove o arquivo se existir de uma versão anterior.
447
+
448
+ ## Desinstalar
449
+
450
+ ```bash
451
+ specs stop
452
+ rm -rf .specs/.run .specs/.jobs.json .specs/config.json
453
+ npm rm -g @mir-code/specs-platform
454
+ # .agents/ e os symlinks .claude//.cursor/ podem permanecer — os agents funcionam sem a UI.
455
+ # .specs/specs/ também permanece — é o seu conteúdo.
456
+ ```
457
+
458
+ ## Licença
459
+
460
+ MIT
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # specs-usage-statusline.sh — captura o uso do plano a partir do statusLine do Claude Code.
3
+ #
4
+ # POR QUE ISSO EXISTE
5
+ #
6
+ # O medidor "Uso do plano" da Specs Platform lia o histórico que o **app desktop**
7
+ # do Claude grava (`~/Library/Application Support/Claude/plan-usage-history.json`).
8
+ # Esse arquivo só é escrito enquanto o app está aberto — quem trabalha só no
9
+ # terminal fica com um número congelado de dias atrás.
10
+ #
11
+ # Não existe endpoint da API para consultar isso: a Usage & Cost Admin API exige
12
+ # chave de organização do Console e reporta tokens/custo da API, não a janela de
13
+ # 5h/7d do plano. Mas o **Claude Code** recebe o dado a cada resposta e o entrega
14
+ # no JSON do statusLine, em `rate_limits.five_hour` e `rate_limits.seven_day`
15
+ # (só para assinantes Pro/Max, e só depois da primeira resposta da sessão).
16
+ #
17
+ # Este script fica no meio desse caminho: lê o JSON, grava o pedaço de rate limit
18
+ # em ~/.claude/specs-usage.json — que a plataforma observa — e repassa o MESMO
19
+ # JSON para o seu statusLine original, cuja saída ele ecoa sem alterar.
20
+ #
21
+ # USO
22
+ #
23
+ # Em ~/.claude/settings.json:
24
+ #
25
+ # "statusLine": {
26
+ # "type": "command",
27
+ # "command": "bash ~/mircode/specs-platform/scripts/specs-usage-statusline.sh ~/.claude/statusline-command.sh"
28
+ # }
29
+ #
30
+ # Sem statusLine próprio, chame sem argumento: o script só grava o snapshot e
31
+ # imprime uma linha curta com os dois percentuais.
32
+ #
33
+ # Requer `jq`.
34
+
35
+ set -euo pipefail
36
+
37
+ SNAPSHOT="${SPECS_USAGE_SNAPSHOT:-$HOME/.claude/specs-usage.json}"
38
+
39
+ input=$(cat)
40
+
41
+ # --- 1) Grava o snapshot, se o JSON trouxer rate_limits -------------------
42
+ # `// empty` faz o jq não imprimir nada quando o campo está ausente — que é o
43
+ # caso de contas API-key, ou antes da primeira resposta da sessão. Sem dado,
44
+ # não mexemos no arquivo: um snapshot antigo e honesto vale mais que um vazio.
45
+ if command -v jq >/dev/null 2>&1; then
46
+ snapshot=$(printf '%s' "$input" | jq -c \
47
+ '(.rate_limits // empty)
48
+ | select((.five_hour // .seven_day) != null)
49
+ | { measuredAt: (now * 1000 | floor),
50
+ five_hour: (.five_hour // null),
51
+ seven_day: (.seven_day // null) }' 2>/dev/null || true)
52
+
53
+ if [[ -n "${snapshot:-}" ]]; then
54
+ mkdir -p "$(dirname "$SNAPSHOT")"
55
+ # Escrita atômica: a plataforma observa este arquivo e não pode ler um JSON
56
+ # pela metade.
57
+ tmp="${SNAPSHOT}.tmp.$$"
58
+ printf '%s\n' "$snapshot" > "$tmp" && mv -f "$tmp" "$SNAPSHOT"
59
+ fi
60
+ fi
61
+
62
+ # --- 2) Repassa para o statusLine original --------------------------------
63
+ if [[ $# -gt 0 ]]; then
64
+ printf '%s' "$input" | "$@"
65
+ exit $?
66
+ fi
67
+
68
+ # Sem comando encadeado: uma linha mínima, para o statusLine não ficar vazio.
69
+ if command -v jq >/dev/null 2>&1; then
70
+ printf '%s' "$input" | jq -r '
71
+ [ (.model.display_name // "Claude"),
72
+ (if .rate_limits.five_hour.used_percentage != null
73
+ then "5h \(.rate_limits.five_hour.used_percentage | floor)%" else empty end),
74
+ (if .rate_limits.seven_day.used_percentage != null
75
+ then "7d \(.rate_limits.seven_day.used_percentage | floor)%" else empty end)
76
+ ] | join(" · ")'
77
+ fi