@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.
- package/LICENSE +21 -0
- package/README.md +460 -0
- package/assets/specs-usage-statusline.sh +77 -0
- package/assets/template/.agents/README.md +123 -0
- package/assets/template/.agents/RUNTIME.md +64 -0
- package/assets/template/.agents/agents/discovery-agent.md +15 -0
- package/assets/template/.agents/agents/drawing-agent.md +15 -0
- package/assets/template/.agents/agents/feature-runner.md +15 -0
- package/assets/template/.agents/agents/refinement-runner.md +15 -0
- package/assets/template/.agents/agents/refinement.md +15 -0
- package/assets/template/.agents/project.md +158 -0
- package/assets/template/.agents/scripts/check-commit.mjs +70 -0
- package/assets/template/.agents/scripts/validate-spec.mjs +345 -0
- package/assets/template/.agents/skills/discovery-agent/SKILL.md +80 -0
- package/assets/template/.agents/skills/discovery-writing/SKILL.md +76 -0
- package/assets/template/.agents/skills/drawing-agent/SKILL.md +85 -0
- package/assets/template/.agents/skills/drawing-writing/SKILL.md +100 -0
- package/assets/template/.agents/skills/execution-protocol/SKILL.md +110 -0
- package/assets/template/.agents/skills/execution-protocol/references/git.md +104 -0
- package/assets/template/.agents/skills/execution-protocol/references/relatorio-halt.md +103 -0
- package/assets/template/.agents/skills/feature-runner/SKILL.md +81 -0
- package/assets/template/.agents/skills/feature-runner/references/divergencias.md +38 -0
- package/assets/template/.agents/skills/feature-runner/references/implementar.md +80 -0
- package/assets/template/.agents/skills/feature-runner/references/localizar.md +46 -0
- package/assets/template/.agents/skills/mermaid-diagramming/SKILL.md +198 -0
- package/assets/template/.agents/skills/prototype-check/SKILL.md +90 -0
- package/assets/template/.agents/skills/refinement/SKILL.md +136 -0
- package/assets/template/.agents/skills/refinement/references/estrutura.md +27 -0
- package/assets/template/.agents/skills/refinement/references/mapear-terreno.md +46 -0
- package/assets/template/.agents/skills/refinement/references/validar-renderizacao.md +29 -0
- package/assets/template/.agents/skills/refinement-runner/SKILL.md +145 -0
- package/assets/template/.agents/skills/requirements-closure/SKILL.md +236 -0
- package/assets/template/.agents/skills/spec-writing/SKILL.md +258 -0
- package/assets/template/.agents/skills/test-strategy/SKILL.md +176 -0
- package/assets/template/.agents/skills/verification/SKILL.md +143 -0
- package/assets/template/.specs/config.json +39 -0
- package/assets/template/.specs/discoveries/meta.json +4 -0
- package/assets/template/.specs/drawings/meta.json +4 -0
- package/assets/template/.specs/specs/_templates/task-backend.md +75 -0
- package/assets/template/.specs/specs/_templates/task-frontend.md +89 -0
- package/assets/template/.specs/specs/_templates/task-integracao.md +72 -0
- package/assets/template/.specs/specs/exemplo/feat-exemplo-primeira-task.md +82 -0
- package/assets/template/.specs/specs/exemplo/meta.json +8 -0
- package/assets/template/.specs/specs/exemplo/overview.md +45 -0
- package/assets/template/.specs/specs/meta.json +4 -0
- package/assets/template/SPECS.md +213 -0
- package/dist/chunk-SOOETS3N.js +3490 -0
- package/dist/chunk-SOOETS3N.js.map +1 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/ui/assets/abnfDiagram-VCTEODGH-DYXtkHNq.js +2 -0
- package/dist/ui/assets/abnfDiagram-VCTEODGH-DYXtkHNq.js.map +1 -0
- package/dist/ui/assets/arc-CJs0gz4E.js +2 -0
- package/dist/ui/assets/arc-CJs0gz4E.js.map +1 -0
- package/dist/ui/assets/architectureDiagram-5GKGNRK7-Br7ZNRfe.js +37 -0
- package/dist/ui/assets/architectureDiagram-5GKGNRK7-Br7ZNRfe.js.map +1 -0
- package/dist/ui/assets/blockDiagram-I7D4REHJ-172V7R5o.js +130 -0
- package/dist/ui/assets/blockDiagram-I7D4REHJ-172V7R5o.js.map +1 -0
- package/dist/ui/assets/c4Diagram-7LVT6UL2-B4AYFhRs.js +39 -0
- package/dist/ui/assets/c4Diagram-7LVT6UL2-B4AYFhRs.js.map +1 -0
- package/dist/ui/assets/channel-BfWO3B41.js +2 -0
- package/dist/ui/assets/channel-BfWO3B41.js.map +1 -0
- package/dist/ui/assets/chunk-2Q5K7J3B-B-u7gtZN.js +2 -0
- package/dist/ui/assets/chunk-2Q5K7J3B-B-u7gtZN.js.map +1 -0
- package/dist/ui/assets/chunk-5VM5RSS4-CjFDq_cq.js +16 -0
- package/dist/ui/assets/chunk-5VM5RSS4-CjFDq_cq.js.map +1 -0
- package/dist/ui/assets/chunk-F27PBJKO-BDYeB7B8.js +2 -0
- package/dist/ui/assets/chunk-F27PBJKO-BDYeB7B8.js.map +1 -0
- package/dist/ui/assets/chunk-IMKFNOWR-y2kXPnac.js +232 -0
- package/dist/ui/assets/chunk-IMKFNOWR-y2kXPnac.js.map +1 -0
- package/dist/ui/assets/chunk-JWPE2WC7-DyCryvDN.js +2 -0
- package/dist/ui/assets/chunk-JWPE2WC7-DyCryvDN.js.map +1 -0
- package/dist/ui/assets/chunk-POPQ4Y6H-DeaxKHvR.js +2 -0
- package/dist/ui/assets/chunk-POPQ4Y6H-DeaxKHvR.js.map +1 -0
- package/dist/ui/assets/chunk-SVP7TREG-CmcZEncL.js +89 -0
- package/dist/ui/assets/chunk-SVP7TREG-CmcZEncL.js.map +1 -0
- package/dist/ui/assets/chunk-TICWLB2K-Df1RRXWR.js +207 -0
- package/dist/ui/assets/chunk-TICWLB2K-Df1RRXWR.js.map +1 -0
- package/dist/ui/assets/chunk-XXDRQBXY-ClTat8Q0.js +2 -0
- package/dist/ui/assets/chunk-XXDRQBXY-ClTat8Q0.js.map +1 -0
- package/dist/ui/assets/classDiagram-ZZMXUADV-YTUiu8rF.js +2 -0
- package/dist/ui/assets/classDiagram-ZZMXUADV-YTUiu8rF.js.map +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-YTUiu8rF.js +2 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-YTUiu8rF.js.map +1 -0
- package/dist/ui/assets/cose-bilkent-JH36ORCC-QvqQLoy5.js +2 -0
- package/dist/ui/assets/cose-bilkent-JH36ORCC-QvqQLoy5.js.map +1 -0
- package/dist/ui/assets/cynefin-OW5HDTMX-BY6w3pAQ.js +179 -0
- package/dist/ui/assets/cynefin-OW5HDTMX-BY6w3pAQ.js.map +1 -0
- package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-luPP4ha1.js +63 -0
- package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-luPP4ha1.js.map +1 -0
- package/dist/ui/assets/cytoscape.esm-Ix0LnXOy.js +332 -0
- package/dist/ui/assets/cytoscape.esm-Ix0LnXOy.js.map +1 -0
- package/dist/ui/assets/dagre-GXQ25YYZ-DhpPwKqq.js +5 -0
- package/dist/ui/assets/dagre-GXQ25YYZ-DhpPwKqq.js.map +1 -0
- package/dist/ui/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/dist/ui/assets/defaultLocale-DX6XiGOO.js.map +1 -0
- package/dist/ui/assets/diagram-S7CK7UJ4-B9wvQM0w.js +31 -0
- package/dist/ui/assets/diagram-S7CK7UJ4-B9wvQM0w.js.map +1 -0
- package/dist/ui/assets/diagram-UQ7AKVKN-gm1M_iZS.js +42 -0
- package/dist/ui/assets/diagram-UQ7AKVKN-gm1M_iZS.js.map +1 -0
- package/dist/ui/assets/diagram-VSXAHHWV-DkEB6nEx.js +4 -0
- package/dist/ui/assets/diagram-VSXAHHWV-DkEB6nEx.js.map +1 -0
- package/dist/ui/assets/diagram-VX7I27RA-Ce2F1bHi.js +25 -0
- package/dist/ui/assets/diagram-VX7I27RA-Ce2F1bHi.js.map +1 -0
- package/dist/ui/assets/diagram-Z3DM3KII-B5ArCh-E.js +25 -0
- package/dist/ui/assets/diagram-Z3DM3KII-B5ArCh-E.js.map +1 -0
- package/dist/ui/assets/ebnfDiagram-PWID7BFC-DgBM80MD.js +2 -0
- package/dist/ui/assets/ebnfDiagram-PWID7BFC-DgBM80MD.js.map +1 -0
- package/dist/ui/assets/erDiagram-RLTQ6QDP-C161af8G.js +100 -0
- package/dist/ui/assets/erDiagram-RLTQ6QDP-C161af8G.js.map +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-B8N_WyW5.js +169 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-B8N_WyW5.js.map +1 -0
- package/dist/ui/assets/ganttDiagram-EL5Y4UJY-CD2RJpTb.js +293 -0
- package/dist/ui/assets/ganttDiagram-EL5Y4UJY-CD2RJpTb.js.map +1 -0
- package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-BrDAzeFT.js +107 -0
- package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-BrDAzeFT.js.map +1 -0
- package/dist/ui/assets/index-CVWRdirI.css +32 -0
- package/dist/ui/assets/index-DoR97Zqf.js +408 -0
- package/dist/ui/assets/index-DoR97Zqf.js.map +1 -0
- package/dist/ui/assets/infoDiagram-27XIBGKW-HYGaMzrA.js +3 -0
- package/dist/ui/assets/infoDiagram-27XIBGKW-HYGaMzrA.js.map +1 -0
- package/dist/ui/assets/init-Gi6I4Gst.js +2 -0
- package/dist/ui/assets/init-Gi6I4Gst.js.map +1 -0
- package/dist/ui/assets/ishikawaDiagram-5VMMS53U-DXTLlBiw.js +71 -0
- package/dist/ui/assets/ishikawaDiagram-5VMMS53U-DXTLlBiw.js.map +1 -0
- package/dist/ui/assets/journeyDiagram-3NMN7TZE-DSJ8QN1S.js +140 -0
- package/dist/ui/assets/journeyDiagram-3NMN7TZE-DSJ8QN1S.js.map +1 -0
- package/dist/ui/assets/kanban-definition-UXKFOSKX-Bm5iz0k3.js +90 -0
- package/dist/ui/assets/kanban-definition-UXKFOSKX-Bm5iz0k3.js.map +1 -0
- package/dist/ui/assets/katex-C5jXJg4s.js +258 -0
- package/dist/ui/assets/katex-C5jXJg4s.js.map +1 -0
- package/dist/ui/assets/layout-DfJgW6eG.js +2 -0
- package/dist/ui/assets/layout-DfJgW6eG.js.map +1 -0
- package/dist/ui/assets/linear-B_kyVotV.js +2 -0
- package/dist/ui/assets/linear-B_kyVotV.js.map +1 -0
- package/dist/ui/assets/mermaid-block-CUJSBQFx.js +2 -0
- package/dist/ui/assets/mermaid-block-CUJSBQFx.js.map +1 -0
- package/dist/ui/assets/mermaid.core-ExHG1WnM.js +313 -0
- package/dist/ui/assets/mermaid.core-ExHG1WnM.js.map +1 -0
- package/dist/ui/assets/mindmap-definition-YA3MSWOX-B1Hh9dQz.js +97 -0
- package/dist/ui/assets/mindmap-definition-YA3MSWOX-B1Hh9dQz.js.map +1 -0
- package/dist/ui/assets/ordinal-Cboi1Yqb.js +2 -0
- package/dist/ui/assets/ordinal-Cboi1Yqb.js.map +1 -0
- package/dist/ui/assets/pegDiagram-XKGWAZYB-x242yOd7.js +2 -0
- package/dist/ui/assets/pegDiagram-XKGWAZYB-x242yOd7.js.map +1 -0
- package/dist/ui/assets/pieDiagram-E7YTZNPT-Dl-YP7Xs.js +40 -0
- package/dist/ui/assets/pieDiagram-E7YTZNPT-Dl-YP7Xs.js.map +1 -0
- package/dist/ui/assets/quadrantDiagram-AXDQQJYC-CMJ920V9.js +8 -0
- package/dist/ui/assets/quadrantDiagram-AXDQQJYC-CMJ920V9.js.map +1 -0
- package/dist/ui/assets/railroadDiagram-O6MQD6OU-1kV83y5t.js +2 -0
- package/dist/ui/assets/railroadDiagram-O6MQD6OU-1kV83y5t.js.map +1 -0
- package/dist/ui/assets/requirementDiagram-BXWQKSXE-BFd8ZZD3.js +85 -0
- package/dist/ui/assets/requirementDiagram-BXWQKSXE-BFd8ZZD3.js.map +1 -0
- package/dist/ui/assets/sankeyDiagram-P5KCCOFB-C0LZevnf.js +41 -0
- package/dist/ui/assets/sankeyDiagram-P5KCCOFB-C0LZevnf.js.map +1 -0
- package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-BasIXLJ0.js +163 -0
- package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-BasIXLJ0.js.map +1 -0
- package/dist/ui/assets/sizeCapture-INFHLROL-qAtnqB7N.js +2 -0
- package/dist/ui/assets/sizeCapture-INFHLROL-qAtnqB7N.js.map +1 -0
- package/dist/ui/assets/stateDiagram-D77RDMKH-CllWDWZP.js +2 -0
- package/dist/ui/assets/stateDiagram-D77RDMKH-CllWDWZP.js.map +1 -0
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-Dwntf8Tk.js +2 -0
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-Dwntf8Tk.js.map +1 -0
- package/dist/ui/assets/swimlanes-42K2YHIH-Bgt0KslH.js +2 -0
- package/dist/ui/assets/swimlanes-42K2YHIH-Bgt0KslH.js.map +1 -0
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-C9abicdw.js +9 -0
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-C9abicdw.js.map +1 -0
- package/dist/ui/assets/timeline-definition-24CTP7MA-BYO2h9Qr.js +121 -0
- package/dist/ui/assets/timeline-definition-24CTP7MA-BYO2h9Qr.js.map +1 -0
- package/dist/ui/assets/vennDiagram-4TSXK5OY-ZzyiwIUi.js +35 -0
- package/dist/ui/assets/vennDiagram-4TSXK5OY-ZzyiwIUi.js.map +1 -0
- package/dist/ui/assets/wardleyDiagram-VM6X3IG4-DfjKCK0N.js +79 -0
- package/dist/ui/assets/wardleyDiagram-VM6X3IG4-DfjKCK0N.js.map +1 -0
- package/dist/ui/assets/xychartDiagram-S5SC5T6Z-Z7daOfmM.js +8 -0
- package/dist/ui/assets/xychartDiagram-S5SC5T6Z-Z7daOfmM.js.map +1 -0
- package/dist/ui/favicon.svg +6 -0
- package/dist/ui/index.html +21 -0
- package/package.json +70 -0
- package/scripts/copy-ui.mjs +21 -0
- 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
|