@payconductor/houndsman 0.1.0 → 0.1.3
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/README.md +68 -89
- package/assets/logo-legacy.png +0 -0
- package/assets/logo.png +0 -0
- package/package.json +3 -2
- package/src/cli/index.ts +101 -10
- package/src/core/assets.ts +11 -0
- package/src/core/brief.ts +27 -5
- package/src/core/commands/findings.ts +618 -0
- package/src/core/commands/review.ts +2 -0
- package/src/core/commands/scaffold.ts +33 -44
- package/src/core/config.ts +42 -0
- package/src/core/diff-source.ts +140 -0
- package/src/core/file-classifier.ts +50 -2
- package/src/core/finding-anchor.ts +147 -0
- package/src/core/findings-state.ts +556 -0
- package/src/core/fix-batch.ts +236 -0
- package/src/core/git-revision.ts +105 -0
- package/src/core/jev/index.ts +1 -0
- package/src/core/jev/jev-client.ts +72 -11
- package/src/core/jev/jev-detector.ts +21 -19
- package/src/core/jev/jev-verify.ts +178 -0
- package/src/core/orchestrator.ts +362 -23
- package/src/core/paths.ts +9 -0
- package/src/core/registry-check.ts +15 -0
- package/src/core/report-builder.ts +118 -9
- package/src/core/run-status.ts +123 -0
- package/src/core/types.ts +10 -0
- package/src/core/verify.ts +175 -0
- package/src/core/watchdog-registry.ts +1 -2
- package/src/index.ts +78 -2
- package/src/server/artifacts.ts +67 -0
- package/src/server/client/App.tsx +158 -0
- package/src/server/client/ArtifactsPanel.tsx +48 -0
- package/src/server/client/Filters.tsx +83 -0
- package/src/server/client/FindingCard.tsx +122 -0
- package/src/server/client/FindingsList.tsx +54 -0
- package/src/server/client/RunList.tsx +52 -0
- package/src/server/client/SummaryBar.tsx +21 -0
- package/src/server/client/api.ts +37 -0
- package/src/server/client/constants.ts +94 -0
- package/src/server/client/index.tsx +12 -0
- package/src/server/client/types.ts +52 -0
- package/src/server/client-build.ts +47 -0
- package/src/server/index.ts +376 -0
- package/src/skill/AUDIT-PROTOCOL.md +2 -2
- package/src/skill/README.md +43 -11
- package/src/skill/SETUP.md +16 -4
- package/src/skill/SKILL.md +48 -0
- package/src/skill/fix.md +206 -0
- package/src/skill/{houndsman.md → hunt.md} +83 -33
- package/src/skill/{houndsman-update.md → train.md} +12 -12
- package/src/skill/verify.md +70 -0
- package/src/tests/findings-anchor.test.ts +120 -0
- package/src/tests/findings-commands.test.ts +476 -0
- package/src/tests/findings-state.test.ts +325 -0
- package/src/tests/fix-batch.test.ts +257 -0
- package/src/tests/review-scripts.test.ts +299 -0
- package/src/tests/run-status.test.ts +147 -0
- package/src/tests/scaffold.test.ts +59 -0
- package/src/tests/server.test.ts +242 -0
- package/src/tests/verify.test.ts +170 -0
package/README.md
CHANGED
|
@@ -12,7 +12,8 @@ relatório único.
|
|
|
12
12
|
|
|
13
13
|
**Terminologia:** **houndsman** = orquestrador/treinador (CLI + motor) · **watchdog** = cada
|
|
14
14
|
agente especialista por domínio que audita o diff · **review/revisão** = o processo ·
|
|
15
|
-
**Jev** = pré-análise opcional.
|
|
15
|
+
**Jev** = pré-análise opcional. O vocabulário completo (brief, findings, manifest, budget,
|
|
16
|
+
overflow, churn, candidato…) está em [`docs/GLOSSARY.md`](docs/GLOSSARY.md).
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -25,8 +26,8 @@ diferente das PRs. O motor só coordena a caçada — as regras são problema se
|
|
|
25
26
|
|
|
26
27
|
- **Bun** — runtime obrigatório.
|
|
27
28
|
- **git** — o diff usa `git diff <base>...<head>`.
|
|
28
|
-
- Opcionais
|
|
29
|
-
|
|
29
|
+
- Opcionais: **`gh`** (contexto da PR) e **`TYPESAFE_API_KEY`** (pré-análise Jev). A revisão
|
|
30
|
+
funciona normalmente sem eles; o `houndsman doctor` valida a credencial Jev quando presente.
|
|
30
31
|
|
|
31
32
|
### 2. Instale o CLI (global)
|
|
32
33
|
|
|
@@ -51,15 +52,13 @@ Cria a camada `.houndsman/`, que é **autocontida** (não instala nada no seu pr
|
|
|
51
52
|
| `.houndsman/exclusions.ts` | você | Arquivos que nunca entram na revisão |
|
|
52
53
|
| `.houndsman/doc-mappings.ts` | você | path → documentação de contexto |
|
|
53
54
|
| `.houndsman/core/**` | auto | Tipos + docs do skill (sincronizados por `houndsman update`) |
|
|
54
|
-
| `.agents/` · `.claude/` | auto | Ponteiros de skill dos harnesses |
|
|
55
|
-
| `package.json` | — | Adiciona o script `"houndsman"` |
|
|
56
55
|
|
|
57
|
-
>
|
|
58
|
-
>
|
|
56
|
+
> O `setup` já cria a camada inicial com um watchdog de exemplo — troque o exemplo pelas
|
|
57
|
+
> regras reais do seu projeto (ou use `/houndsman train`).
|
|
59
58
|
|
|
60
59
|
### 4. Escreva suas regras
|
|
61
60
|
|
|
62
|
-
Edite `.houndsman/watchdogs/` — veja [Escrevendo boas regras](#escrevendo-boas-regras).
|
|
61
|
+
Edite `.houndsman/watchdogs/` — veja [Escrevendo boas regras](docs/AVANCADO.md#escrevendo-boas-regras).
|
|
63
62
|
|
|
64
63
|
### 5. Valide
|
|
65
64
|
|
|
@@ -72,7 +71,7 @@ bun run houndsman doctor
|
|
|
72
71
|
No seu harness (Claude Code / agents), invoque o skill:
|
|
73
72
|
|
|
74
73
|
```
|
|
75
|
-
/houndsman
|
|
74
|
+
/houndsman hunt
|
|
76
75
|
```
|
|
77
76
|
|
|
78
77
|
O skill orquestra tudo e grava o relatório em `.houndsman/.runs/<branch-slug>/review.md`.
|
|
@@ -83,25 +82,68 @@ Não precisa rodar nenhum script à mão.
|
|
|
83
82
|
## O fluxo de trabalho
|
|
84
83
|
|
|
85
84
|
```
|
|
86
|
-
/houndsman
|
|
85
|
+
/houndsman hunt
|
|
87
86
|
├─ Passo 0 contexto da PR (gh, opcional)
|
|
88
|
-
├─ Passo 1 classifica o diff → briefs por watchdog
|
|
87
|
+
├─ Passo 1 classifica o diff → briefs por watchdog (incremental quando possível)
|
|
89
88
|
├─ Passo 2 N subagentes revisam em paralelo
|
|
90
|
-
|
|
89
|
+
├─ Passo 3 consolida em review.md + state.json
|
|
90
|
+
├─ Passo 4 valida na UI (server) e marca os achados a executar
|
|
91
|
+
├─ Passo 5 fix corrige o grupo atual → run.json (fix_done)
|
|
92
|
+
└─ Passo 6 verify confere o que resolveu, sem re-revisar
|
|
91
93
|
```
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
O skill tem **4 capabilities**, todas em `/houndsman`:
|
|
96
|
+
|
|
97
|
+
- **`hunt`** — revisa a PR (default: `/houndsman` = `/houndsman hunt`).
|
|
98
|
+
- **`train`** — mantém/atualiza as regras dos watchdogs.
|
|
99
|
+
- **`fix`** — corrige os findings marcados (`/houndsman fix`).
|
|
100
|
+
- **`verify`** — confere se as violações foram resolvidas (`/houndsman verify`), sem re-revisar.
|
|
101
|
+
|
|
102
|
+
- Revisão normal: `/houndsman` ou `/houndsman hunt` (a branch é detectada; base default `main`).
|
|
103
|
+
- Revisar outra branch: `/houndsman hunt feature/minha-branch`.
|
|
104
|
+
- Validar e triar na UI: `houndsman server --open` — marque os achados a executar (`execute`).
|
|
95
105
|
- Manual (sem skill): `houndsman context` → `houndsman --base main` → *(subagentes)* →
|
|
96
106
|
`houndsman --finalize`.
|
|
97
107
|
- CI: `houndsman --finalize --fail-on-blocking` (exit 1 se houver bloqueantes).
|
|
98
108
|
|
|
99
109
|
---
|
|
100
110
|
|
|
111
|
+
## Triando os findings (opcional)
|
|
112
|
+
|
|
113
|
+
Depois do `--finalize`, cada finding tem um `id` curto e um `fingerprint` de **conteúdo**
|
|
114
|
+
no `.houndsman/.runs/<branch-slug>/state.json`. Você decide o que fazer com cada um — pela
|
|
115
|
+
**UI local** ou pelo **agente** (auto-fix) — sem abrir o JSON à mão:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bun run houndsman server --open # UI: triar, comentar, marcar feito, ler artefatos
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- **decision:** `pending` · `execute` · `ignore` (+ comentário).
|
|
122
|
+
- **execState:** `pending` · `in_progress` · `done` · `stale`.
|
|
123
|
+
|
|
124
|
+
**Auto-fix pelo agente** — as linhas mudam depois do fix, e o motor reancora por conteúdo:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
bun run houndsman findings batch --json # grupo atual (agrupamento automático) + outros
|
|
128
|
+
# ... o agente corrige apenas esse grupo ...
|
|
129
|
+
bun run houndsman findings sync # reancora por conteúdo
|
|
130
|
+
bun run houndsman findings done <id>
|
|
131
|
+
bun run houndsman findings verify # confere o que resolveu (barato, sem re-revisar)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
> A partir da 2ª revisão o `hunt` é **incremental**: revisa só os arquivos que mudaram desde
|
|
135
|
+
> o último `--finalize` e **carrega** os findings dos arquivos inalterados. `--full` força
|
|
136
|
+
> o review completo.
|
|
137
|
+
|
|
138
|
+
> A identidade do finding é o **conteúdo** (fingerprint), não a linha: itens deslocados
|
|
139
|
+
> pelo fix não se perdem. O estado é local e mora em `.runs/<slug>/state.json` (gitignored).
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
101
143
|
## Mantendo a configuração
|
|
102
144
|
|
|
103
|
-
- **Alterar regras** → use
|
|
104
|
-
existe (evidência) antes de editar e roda as validações.
|
|
145
|
+
- **Alterar regras** → use a capability **`train`** (`/houndsman train`). Ela prova no código
|
|
146
|
+
que o padrão existe (evidência) antes de editar e roda as validações.
|
|
105
147
|
- **Atualizou a lib?** Sincronize os arquivos auto-gerados sem tocar nas suas regras:
|
|
106
148
|
|
|
107
149
|
```bash
|
|
@@ -116,80 +158,17 @@ Não precisa rodar nenhum script à mão.
|
|
|
116
158
|
|
|
117
159
|
---
|
|
118
160
|
|
|
119
|
-
## Avançado
|
|
120
|
-
|
|
121
|
-
### Comandos do dia a dia
|
|
122
|
-
|
|
123
|
-
| Comando | Função |
|
|
124
|
-
|---|---|
|
|
125
|
-
| `houndsman setup` | Cria a configuração inicial do projeto |
|
|
126
|
-
| `houndsman update` | Sincroniza os arquivos auto-gerados (`.houndsman/core`) |
|
|
127
|
-
| `houndsman doctor` | Valida a configuração e o ambiente (anti-drift) |
|
|
128
|
-
| `houndsman context` | Passo 0 — contexto da PR via `gh` |
|
|
129
|
-
| `houndsman --base main` | Passo 1 — prepara os briefs |
|
|
130
|
-
| `houndsman --finalize` | Passo 3 — gera o relatório |
|
|
131
|
-
| `houndsman watchdog <id>` | Contexto de um watchdog (`--rules`, `--json`) |
|
|
132
|
-
|
|
133
|
-
Flags: `--base <branch>` (default `main`) · `--head <ref>` (default `HEAD`) ·
|
|
134
|
-
`--branch <nome>` · `--dir <dir>` · `--config <path>` · `--clean` · `--no-jev` ·
|
|
135
|
-
`--fail-on-blocking` (com `--finalize`).
|
|
136
|
-
|
|
137
|
-
### Escrevendo boas regras
|
|
138
|
-
|
|
139
|
-
O texto da regra é renderizado **literalmente** no brief do subagente — ambiguidade vira
|
|
140
|
-
ruído na revisão. Toda regra começa com um **prefixo de polaridade**:
|
|
141
|
-
|
|
142
|
-
- `Promover:` → algo que **DEVE** acontecer (o padrão precisa existir no código).
|
|
143
|
-
- `Evitar:` → algo que **NÃO DEVE** acontecer. `Não` e `Nunca` no início são equivalentes.
|
|
144
|
-
|
|
145
|
-
Boas práticas:
|
|
146
|
-
|
|
147
|
-
1. **Só regra que o projeto aplica hoje** — prove com evidência (`arquivo:linha` para
|
|
148
|
-
afirmativas; `0 ocorrências via grep` do anti-padrão para negativas). Regra afirmativa sem
|
|
149
|
-
aplicação real gera falso positivo.
|
|
150
|
-
2. **Geral e reutilizável** — descreva um padrão do domínio, não uma regra de negócio
|
|
151
|
-
amarrada a uma única classe/use-case (isso é papel de teste automatizado).
|
|
152
|
-
3. **Condição inequívoca e critério concreto** — duas IAs chegam à mesma conclusão; use
|
|
153
|
-
limite/lista explícita ou símbolo real do código.
|
|
154
|
-
4. **Sem jargão vago nem qualificador vazio** — nada de "o antigo", "isso", "apenas por
|
|
155
|
-
compatibilidade".
|
|
156
|
-
5. **Declare o `classify`** do watchdog — regex de path (`filename`) ou de conteúdo
|
|
157
|
-
(`content`) para o motor achar os arquivos do domínio.
|
|
158
|
-
6. **Prefira editar via `/houndsman-update`** — ele aplica a rubrica completa e roda as
|
|
159
|
-
verificações (`doctor`, `test`).
|
|
160
|
-
|
|
161
|
-
> `houndsman doctor` emite um aviso (não bloqueante) para regra sem prefixo de polaridade.
|
|
162
|
-
|
|
163
|
-
Exemplo:
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
{ severity: "blocking", text: "Evitar: handler de webhook processa o payload antes de validar a assinatura do provider." }
|
|
167
|
-
{ severity: "attention", text: "Promover: rotas validam a entrada com o schema do domínio." }
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Estrutura gerada por `setup`
|
|
171
|
-
|
|
172
|
-
```
|
|
173
|
-
houndsman.config.ts
|
|
174
|
-
.houndsman/
|
|
175
|
-
├── .gitignore # AUTO
|
|
176
|
-
├── core/ # AUTO — sincronizado por `update`
|
|
177
|
-
│ ├── types.ts
|
|
178
|
-
│ └── skill/*.md
|
|
179
|
-
├── watchdogs/ # você — 1 módulo .ts por watchdog
|
|
180
|
-
│ └── index.ts
|
|
181
|
-
├── exclusions.ts # você
|
|
182
|
-
└── doc-mappings.ts # você
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Os artefatos de cada review ficam em `.houndsman/.runs/<branch-slug>/` (auto-ignorados).
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
161
|
## Documentação
|
|
190
162
|
|
|
191
|
-
-
|
|
163
|
+
- `docs/AVANCADO.md` — como funciona por dentro (diagrama do fluxo e onde o Jev entra),
|
|
164
|
+
referência de comandos e boas práticas para escrever regras.
|
|
165
|
+
- `docs/GLOSSARY.md` — glossário do vocabulário de domínio (brief, findings, manifest,
|
|
166
|
+
budget, overflow, churn, candidato, Jev…): o que cada termo significa **neste** projeto.
|
|
167
|
+
- `docs/CONTRIBUTING.md` — arquitetura, fluxos internos, princípios e desenvolvimento da
|
|
192
168
|
lib (para quem vai contribuir).
|
|
169
|
+
|
|
193
170
|
- `src/skill/SETUP.md` — referência de configuração passo a passo.
|
|
194
|
-
- `src/skill/
|
|
195
|
-
- `src/skill/
|
|
171
|
+
- `src/skill/SKILL.md` — entry point do skill `/houndsman` (capabilities).
|
|
172
|
+
- `src/skill/hunt.md` — capability `hunt` (revisão de PR).
|
|
173
|
+
- `src/skill/train.md` — capability `train` (manutenção das regras).
|
|
174
|
+
- `src/skill/fix.md` — capability `fix` (correção dos findings).
|
|
Binary file
|
package/assets/logo.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payconductor/houndsman",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Orquestrador de revisão de Pull Requests: classifica o diff, despacha watchdogs especializados por domínio e consolida o relatório.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"ci:format:fix": "biome format --fix"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"axios": "^1.12.2"
|
|
57
|
+
"axios": "^1.12.2",
|
|
58
|
+
"preact": "^11.0.1"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@biomejs/biome": "2.2.4",
|
package/src/cli/index.ts
CHANGED
|
@@ -11,17 +11,20 @@
|
|
|
11
11
|
* (default) Passo 1 — preparar (classificação + briefs + manifesto)
|
|
12
12
|
* --finalize Passo 3 — consolidar os findings em review.md
|
|
13
13
|
* context Passo 0 — coletar o contexto da PR via `gh`
|
|
14
|
+
* findings <sub> Triagem/correção dos findings (list/set/done/sync/batch/verify/...)
|
|
15
|
+
* server Sobe a UI local de triagem (Bun.serve)
|
|
14
16
|
* setup Cria a configuração inicial do projeto (config + skills)
|
|
15
17
|
* update Sincroniza os arquivos auto-gerados (.houndsman/core)
|
|
16
|
-
* doctor Valida a configuração
|
|
18
|
+
* doctor Valida a configuração, o ambiente e a credencial Jev (anti-drift)
|
|
17
19
|
* test Roda a suíte de testes da lib
|
|
18
20
|
* watchdog <id> Contexto de um watchdog (`--rules`, `--json`)
|
|
19
21
|
*
|
|
20
22
|
* Flags: `--base <b>` · `--head <ref>` · `--branch <b>` · `--dir <d>` ·
|
|
21
|
-
* `--config <path>` · `--clean` · `--no-jev` · `--fail-on-blocking`.
|
|
23
|
+
* `--config <path>` · `--clean` · `--full` · `--no-jev` · `--fail-on-blocking`.
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
import { runContextCommand } from "../core/commands/context";
|
|
27
|
+
import { runFindingsCommand, runVerifyCommand } from "../core/commands/findings";
|
|
25
28
|
import { parseReviewArgs } from "../core/commands/review";
|
|
26
29
|
import { runSetupCommand } from "../core/commands/setup";
|
|
27
30
|
import { runLibTests } from "../core/commands/test";
|
|
@@ -29,9 +32,11 @@ import { runUpdateCommand } from "../core/commands/update";
|
|
|
29
32
|
import { renderWatchdogContext } from "../core/commands/watchdog";
|
|
30
33
|
import { type HoundsmanConfig, resolveArtifactDir } from "../core/config";
|
|
31
34
|
import { loadConfig } from "../core/config-loader";
|
|
35
|
+
import { JevClient } from "../core/jev";
|
|
32
36
|
import { finalize, prepare, resolveBranch } from "../core/orchestrator";
|
|
33
37
|
import { relFromRoot } from "../core/paths";
|
|
34
38
|
import { checkRegistry } from "../core/registry-check";
|
|
39
|
+
import { startServer } from "../server";
|
|
35
40
|
|
|
36
41
|
const HELP = `Uso: houndsman [comando] [flags]
|
|
37
42
|
|
|
@@ -39,9 +44,11 @@ Comandos:
|
|
|
39
44
|
(nenhum) Prepara a revisão (classification + briefs + manifesto)
|
|
40
45
|
--finalize Consolida os findings em review.md
|
|
41
46
|
context Coleta o contexto da PR via gh (Passo 0)
|
|
47
|
+
findings <sub> Triagem/correção dos findings (list · show · set · done · reset · sync · summary · batch · verify · path)
|
|
48
|
+
server Sobe a UI local de triagem (--port, --host, --open)
|
|
42
49
|
setup Cria a configuração inicial do projeto (config + skills)
|
|
43
50
|
update Sincroniza os arquivos auto-gerados (.houndsman/core)
|
|
44
|
-
doctor Valida a configuração
|
|
51
|
+
doctor Valida a configuração, o ambiente e a credencial Jev
|
|
45
52
|
test Roda a suíte de testes da lib
|
|
46
53
|
watchdog <id> Contexto de um watchdog (--rules, --json)
|
|
47
54
|
|
|
@@ -52,8 +59,25 @@ Flags:
|
|
|
52
59
|
--branch <nome> Branch alvo (default: detecta do git)
|
|
53
60
|
--dir <dir> Diretório dos artefatos
|
|
54
61
|
--clean Descarta findings anteriores
|
|
62
|
+
--full Força review completo (ignora o modo incremental)
|
|
55
63
|
--no-jev Desativa a pré-análise Jev
|
|
56
|
-
--fail-on-blocking Exit 1 se houver bloqueantes (com --finalize)
|
|
64
|
+
--fail-on-blocking Exit 1 se houver bloqueantes (com --finalize)
|
|
65
|
+
|
|
66
|
+
Flags de findings:
|
|
67
|
+
--decision <d> pending | execute | ignore
|
|
68
|
+
--exec <e> pending | in_progress | done | stale
|
|
69
|
+
--comment <text> Comentário/instrução adicional
|
|
70
|
+
--watchdog <id> Filtra por watchdog (list)
|
|
71
|
+
--severity <s> Filtra por severidade (list, batch)
|
|
72
|
+
--file <text> Filtra por trecho do caminho (list)
|
|
73
|
+
--all Inclui findings ausentes do último relatório
|
|
74
|
+
--json Saída JSON
|
|
75
|
+
--limit <n> Limita o nº de itens (list)
|
|
76
|
+
--group <key> Grupo alvo no batch (default: o primeiro)
|
|
77
|
+
--offset <n> Paginação dentro do grupo (batch)
|
|
78
|
+
--include-done Inclui já concluídos no batch
|
|
79
|
+
--apply (verify) Marca os resolvidos como done
|
|
80
|
+
--fail-on-persisting (verify) Exit 1 se algum blocking persistir`;
|
|
57
81
|
|
|
58
82
|
function readFlag(args: string[], flag: string): string | undefined {
|
|
59
83
|
const index = args.indexOf(flag);
|
|
@@ -88,8 +112,8 @@ function runContext(config: HoundsmanConfig, args: string[]): void {
|
|
|
88
112
|
}
|
|
89
113
|
}
|
|
90
114
|
|
|
91
|
-
/** Valida a configuração do projeto e o ambiente
|
|
92
|
-
function runDoctor(config: HoundsmanConfig): void {
|
|
115
|
+
/** Valida a configuração do projeto e o ambiente. */
|
|
116
|
+
async function runDoctor(config: HoundsmanConfig): Promise<void> {
|
|
93
117
|
console.log("→ Validando configuração do projeto...");
|
|
94
118
|
const { problems, warnings, jevQuestions } = checkRegistry(config);
|
|
95
119
|
|
|
@@ -110,9 +134,27 @@ function runDoctor(config: HoundsmanConfig): void {
|
|
|
110
134
|
console.log("\n→ Ambiente:");
|
|
111
135
|
console.log(` ${Bun.which("git") ? "✅" : "❌"} git`);
|
|
112
136
|
console.log(` ${Bun.which("gh") ? "✅" : "⚠️ "} gh (opcional — contexto da PR)`);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
137
|
+
await reportJevCredential();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Reporta o estado da credencial Jev: presença e validade (chamada mínima).
|
|
142
|
+
* Fail-open — nunca altera o exit code (`doctor` é diagnóstico).
|
|
143
|
+
*/
|
|
144
|
+
async function reportJevCredential(): Promise<void> {
|
|
145
|
+
const client = new JevClient();
|
|
146
|
+
if (!client.isConfigured()) {
|
|
147
|
+
console.log(" ⚠️ TYPESAFE_API_KEY (opcional — pré-análise Jev)");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const check = await client.validateCredential();
|
|
151
|
+
if (check.ok) {
|
|
152
|
+
console.log(` ✅ Jev: credencial válida (modelo ${check.model}).`);
|
|
153
|
+
} else if (check.kind === "invalid") {
|
|
154
|
+
console.log(` ❌ Jev: credencial inválida — ${check.detail}`);
|
|
155
|
+
} else {
|
|
156
|
+
console.log(` ⚠️ Jev: não foi possível validar — ${check.detail}`);
|
|
157
|
+
}
|
|
116
158
|
}
|
|
117
159
|
|
|
118
160
|
/** Contexto de um watchdog. */
|
|
@@ -137,6 +179,49 @@ function runWatchdog(config: HoundsmanConfig, args: string[]): void {
|
|
|
137
179
|
);
|
|
138
180
|
}
|
|
139
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Subcomando `findings` — triagem/correção dos findings (list/show/set/done/reset/
|
|
184
|
+
* sync/summary/batch/verify/path). Deixa o agente interagir com o estado sem abrir
|
|
185
|
+
* o JSON. O `verify` é assíncrono (consulta opcional ao Jev) — roteado à parte.
|
|
186
|
+
*/
|
|
187
|
+
async function runFindings(config: HoundsmanConfig, args: string[]): Promise<void> {
|
|
188
|
+
const sub = args.find((arg) => !arg.startsWith("-"));
|
|
189
|
+
const result =
|
|
190
|
+
sub === "verify"
|
|
191
|
+
? await runVerifyCommand(config, args)
|
|
192
|
+
: runFindingsCommand(config, args);
|
|
193
|
+
if (result.exitCode !== 0) {
|
|
194
|
+
console.error(result.output);
|
|
195
|
+
process.exit(result.exitCode);
|
|
196
|
+
}
|
|
197
|
+
console.log(result.output);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Abre a URL no navegador padrão (best-effort, por plataforma). */
|
|
201
|
+
function openBrowser(url: string): void {
|
|
202
|
+
try {
|
|
203
|
+
const command =
|
|
204
|
+
process.platform === "win32"
|
|
205
|
+
? ["cmd", "/c", "start", "", url]
|
|
206
|
+
: process.platform === "darwin"
|
|
207
|
+
? ["open", url]
|
|
208
|
+
: ["xdg-open", url];
|
|
209
|
+
Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
|
|
210
|
+
} catch {
|
|
211
|
+
// best-effort — se não abrir, o usuário copia a URL.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Subcomando `server` — sobe a UI local de triagem. */
|
|
216
|
+
function runServer(config: HoundsmanConfig, args: string[]): void {
|
|
217
|
+
const port = Number(readFlag(args, "--port")) || 7331;
|
|
218
|
+
const host = readFlag(args, "--host") ?? "127.0.0.1";
|
|
219
|
+
const { url } = startServer({ config, port, host });
|
|
220
|
+
console.log(`[houndsman] servidor em ${url}`);
|
|
221
|
+
console.log("[houndsman] Ctrl+C para encerrar.");
|
|
222
|
+
if (args.includes("--open")) openBrowser(url);
|
|
223
|
+
}
|
|
224
|
+
|
|
140
225
|
/** Passo 1/3 — preparar ou finalizar. */
|
|
141
226
|
async function runReview(config: HoundsmanConfig, args: string[]): Promise<void> {
|
|
142
227
|
const opts = parseReviewArgs(args, resolveArtifactDir(config));
|
|
@@ -187,11 +272,17 @@ export async function main(): Promise<void> {
|
|
|
187
272
|
return;
|
|
188
273
|
case "doctor":
|
|
189
274
|
case "check":
|
|
190
|
-
runDoctor(config);
|
|
275
|
+
await runDoctor(config);
|
|
191
276
|
return;
|
|
192
277
|
case "watchdog":
|
|
193
278
|
runWatchdog(config, rest);
|
|
194
279
|
return;
|
|
280
|
+
case "findings":
|
|
281
|
+
await runFindings(config, rest);
|
|
282
|
+
return;
|
|
283
|
+
case "server":
|
|
284
|
+
runServer(config, rest);
|
|
285
|
+
return;
|
|
195
286
|
case undefined:
|
|
196
287
|
await runReview(config, rest);
|
|
197
288
|
return;
|
package/src/core/assets.ts
CHANGED
|
@@ -11,10 +11,21 @@ import { join } from "node:path";
|
|
|
11
11
|
/** Diretório de assets markdown embarcados no pacote. */
|
|
12
12
|
export const SKILL_DIR = join(import.meta.dir, "..", "skill");
|
|
13
13
|
|
|
14
|
+
/** Diretório de assets de mídia do pacote (root `assets/` — logo etc.). */
|
|
15
|
+
export const MEDIA_DIR = join(import.meta.dir, "..", "..", "assets");
|
|
16
|
+
|
|
14
17
|
/** Nome do arquivo de schema de output dos watchdogs. */
|
|
15
18
|
export const OUTPUT_FORMAT_FILE = "OUTPUT-FORMAT.md";
|
|
16
19
|
|
|
20
|
+
/** Nome do arquivo-base do skill (`SKILL.md`) — entry point escrito para cada harness. */
|
|
21
|
+
export const SKILL_FILE = "SKILL.md";
|
|
22
|
+
|
|
17
23
|
/** Caminho absoluto de um asset embarcado em `src/skill/`. */
|
|
18
24
|
export function assetPath(fileName: string): string {
|
|
19
25
|
return join(SKILL_DIR, fileName);
|
|
20
26
|
}
|
|
27
|
+
|
|
28
|
+
/** Caminho absoluto de um asset de mídia em `assets/` (ex.: a logo). */
|
|
29
|
+
export function mediaPath(fileName: string): string {
|
|
30
|
+
return join(MEDIA_DIR, fileName);
|
|
31
|
+
}
|
package/src/core/brief.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Renderização do brief de um watchdog (biblioteca) — `defaultRenderBrief`.
|
|
3
3
|
*
|
|
4
|
-
* O procedimento (`PROCEDURE`) é compartilhado por todos os watchdogs; o que
|
|
5
|
-
*
|
|
4
|
+
* O procedimento (`PROCEDURE`) é compartilhado por todos os watchdogs; o que é
|
|
5
|
+
* específico do domínio vive nas regras do watchdog (`rules`).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Procedimento de revisão — compartilhado por todos os watchdogs.
|
|
20
|
-
* Substitui o antigo protocolo genérico de 5 passos + o ponteiro `PROTOCOL_GLOB`.
|
|
21
20
|
*/
|
|
22
21
|
export const PROCEDURE: readonly string[] = [
|
|
23
22
|
"1. **FILTER** — descarte o que não for do seu domínio (ver *Prioridade de leitura* e *Não carregar*).",
|
|
@@ -78,14 +77,35 @@ function optionalList(title: string, items: readonly string[] | undefined): stri
|
|
|
78
77
|
return [`## ${title}`, "", ...items.map((item) => `- ${item}`), ""];
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
/** Lista de arquivos do domínio com status. */
|
|
80
|
+
/** Lista de arquivos do domínio com status (e churn, quando disponível). */
|
|
82
81
|
function fileItems(args: WatchdogRenderArgs): string[] {
|
|
83
82
|
return args.files.map((file) => {
|
|
84
83
|
const status = args.statuses[file]?.status ?? "modified";
|
|
85
|
-
|
|
84
|
+
const churn = args.churn?.[file];
|
|
85
|
+
const delta = churn ? `, +${churn.additions}/−${churn.deletions}` : "";
|
|
86
|
+
return `- \`${file}\` (${status}${delta})`;
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Arquivos do domínio fora do orçamento (triage de PRs grandes): o watchdog NÃO
|
|
92
|
+
* deve lê-los, mas precisa registrá-los em `skipped` — cobertura honesta.
|
|
93
|
+
*/
|
|
94
|
+
function overflowSection(args: WatchdogRenderArgs): string[] {
|
|
95
|
+
const overflow = args.overflow ?? [];
|
|
96
|
+
if (overflow.length === 0) return [];
|
|
97
|
+
return [
|
|
98
|
+
"## Fora do orçamento (NÃO revisar)",
|
|
99
|
+
"",
|
|
100
|
+
`> ${overflow.length} arquivo(s) do seu domínio ficaram fora do orçamento`,
|
|
101
|
+
"> (menor churn). Não os leia: registre-os em `skipped` com o motivo",
|
|
102
|
+
'> "budget" para que a cobertura do relatório fique honesta.',
|
|
103
|
+
"",
|
|
104
|
+
...overflow.map(codeItem),
|
|
105
|
+
"",
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
|
|
89
109
|
/** Seção de deletados (breaking change) — só p/ watchdogs com `breakingChange`. */
|
|
90
110
|
function deletedSection(args: WatchdogRenderArgs, def: WatchdogDefinition): string[] {
|
|
91
111
|
if (!def.breakingChange || args.deleted.length === 0) return [];
|
|
@@ -141,6 +161,8 @@ export function defaultRenderBrief(
|
|
|
141
161
|
"",
|
|
142
162
|
);
|
|
143
163
|
|
|
164
|
+
lines.push(...overflowSection(args));
|
|
165
|
+
|
|
144
166
|
if (args.candidates && args.candidates.length > 0) {
|
|
145
167
|
lines.push(...renderBriefCandidates(args.candidates));
|
|
146
168
|
}
|