@payconductor/houndsman 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -3
- package/assets/houndsman.png +0 -0
- package/package.json +63 -5
- package/src/cli/index.ts +210 -0
- package/src/core/assets.ts +20 -0
- package/src/core/brief.ts +162 -0
- package/src/core/commands/context.ts +39 -0
- package/src/core/commands/review.ts +50 -0
- package/src/core/commands/scaffold.ts +373 -0
- package/src/core/commands/setup.ts +75 -0
- package/src/core/commands/test.ts +20 -0
- package/src/core/commands/update.ts +30 -0
- package/src/core/commands/watchdog.ts +105 -0
- package/src/core/config-loader.ts +87 -0
- package/src/core/config.ts +59 -0
- package/src/core/doc-discoverer.ts +33 -0
- package/src/core/file-classifier.ts +247 -0
- package/src/core/jev/index.ts +12 -0
- package/src/core/jev/jev-candidates.ts +105 -0
- package/src/core/jev/jev-client.ts +99 -0
- package/src/core/jev/jev-detector.ts +203 -0
- package/src/core/jev/jev-questions.ts +92 -0
- package/src/core/jev/jev-types.ts +91 -0
- package/src/core/orchestrator.ts +444 -0
- package/src/core/paths.ts +30 -0
- package/src/core/pr-context.ts +271 -0
- package/src/core/registry-check.ts +145 -0
- package/src/core/report-builder.ts +357 -0
- package/src/core/types.ts +137 -0
- package/src/core/watchdog-registry.ts +43 -0
- package/src/index.ts +56 -0
- package/src/skill/AUDIT-PROTOCOL.md +96 -0
- package/src/skill/OUTPUT-FORMAT.md +70 -0
- package/src/skill/README.md +116 -0
- package/src/skill/SETUP.md +99 -0
- package/src/skill/example-review.md +58 -0
- package/src/skill/houndsman-update.md +251 -0
- package/src/skill/houndsman.md +239 -0
- package/src/tests/fixtures/fake-project.ts +64 -0
- package/src/tests/review-scripts.test.ts +851 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# houndsman — {{projectName}} (v1)
|
|
2
|
+
|
|
3
|
+
> **Documento canônico do skill `/houndsman`.** Os `SKILL.md` de cada harness
|
|
4
|
+
> (`.agents/skills/houndsman/` e `.claude/skills/houndsman/`) são apenas ponteiros para cá.
|
|
5
|
+
|
|
6
|
+
Revisão de PR: classificação de arquivos → revisão paralela por domínio → consolidação.
|
|
7
|
+
O motor é o CLI **`houndsman`**; as regras do projeto ficam em `houndsman.config.ts` + `houndsman/`.
|
|
8
|
+
|
|
9
|
+
**Terminologia:** **houndsman** = orquestrador/treinador (CLI + motor); **watchdog** = cada agente
|
|
10
|
+
especialista por domínio que audita o diff (antigo "revisor"); **review/revisão** = o processo;
|
|
11
|
+
**Jev** = pré-análise opcional (Passo 1.5).
|
|
12
|
+
|
|
13
|
+
## Quem executa o quê
|
|
14
|
+
|
|
15
|
+
- **Você** só invoca o skill: `/houndsman`. Não precisa rodar nenhum script à mão.
|
|
16
|
+
- **O skill** orquestra: executa o CLI `houndsman` e despacha os subagentes.
|
|
17
|
+
- **O CLI** tem passos distintos, um comando cada:
|
|
18
|
+
|
|
19
|
+
| Passo | Comando | Quem roda | Resultado |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| 0. Contexto | `houndsman context` | subagente | `context.json` + `context-brief.md` → `context-summary.json` |
|
|
22
|
+
| 1. Preparar | `houndsman` | skill | `classification.json` + `candidates.json` + `briefs/` + `manifest.json` |
|
|
23
|
+
| 2. Revisar | *(subagentes)* | skill (via `runSubagent`) | `findings/<watchdog>.json` (um por watchdog) |
|
|
24
|
+
| 3. Finalizar | `houndsman --finalize` | skill | `review.md` |
|
|
25
|
+
|
|
26
|
+
> Por que comandos separados? Entre o passo 1 e o 3 acontece trabalho de LLM (os subagentes).
|
|
27
|
+
|
|
28
|
+
**Arquitetura:**
|
|
29
|
+
|
|
30
|
+
- N watchdogs especializados por domínio de negócio.
|
|
31
|
+
- **Cada watchdog declara o que revisa** (`classify` em `.houndsman/watchdogs/<id>.ts`:
|
|
32
|
+
`{ type?: "filename" | "content", value }` — `value` é sempre regex; `filename`
|
|
33
|
+
(padrão) casa o caminho, `content` casa o conteúdo do arquivo). Não há arquivo de
|
|
34
|
+
classificação à parte nem fallback: arquivo fora dos padrões fica `unclassified`.
|
|
35
|
+
- **Fonte única de verdade do projeto:** `houndsman.config.ts` (`HoundsmanConfig`) +
|
|
36
|
+
`.houndsman/` (`watchdogs/<id>.ts`: regras + escopo + foco + `classify`;
|
|
37
|
+
`exclusions.ts`: filtros; `doc-mappings.ts`: docs).
|
|
38
|
+
- **O motor genérico** vive no pacote `@payconductor/houndsman` (`core/` + `cli/`). O markdown
|
|
39
|
+
do skill vive em `src/skill/`.
|
|
40
|
+
- **As regras refletem os padrões APLICADOS hoje** — não validar práticas que o projeto
|
|
41
|
+
não adota. Regra sem aplicação real no código gera falso positivo.
|
|
42
|
+
- Cada watchdog recebe apenas seus arquivos relevantes (brief pronto) — nunca o diff completo.
|
|
43
|
+
- Saída estruturada (JSON) por watchdog → consolidada e deduplicada automaticamente.
|
|
44
|
+
|
|
45
|
+
## Quando Usar
|
|
46
|
+
|
|
47
|
+
- Revisar PR antes de merge
|
|
48
|
+
- Code review de feature branch
|
|
49
|
+
- Validar mudanças antes de push
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Passo 0 — Contexto da PR (subagente)
|
|
54
|
+
|
|
55
|
+
O contexto vem da **descrição da PR** via GitHub CLI (`gh`), quando disponível e logado.
|
|
56
|
+
Um subagente executa o CLI e resume:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Você é o subagente de contexto do houndsman. Execute `houndsman context`;
|
|
60
|
+
leia o `context-brief.md` indicado na saída; siga as instruções e grave
|
|
61
|
+
`context-summary.json`. Retorne `objective`/`motivation`/`changeSummary` ou "indisponível".
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- O CLI detecta `gh` (**ausente/sem login/sem PR** → grava `available:false` e sai com código 0).
|
|
65
|
+
- O subagente resume em `context-summary.json`: `objective` (1 frase, ≤ 200 chars),
|
|
66
|
+
`motivation` (1 frase, ≤ 240 chars) e `changeSummary` (≤ 4 bullets, ≤ 160 chars cada),
|
|
67
|
+
em pt-BR e baseado **somente** no `context.json`. **Curto de propósito.**
|
|
68
|
+
- Sem contexto → a revisão segue normalmente e a seção é omitida (registrado no `manifest.json`).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Passo 1 — Preparar (skill executa)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
houndsman --base main
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Gera em `.houndsman/.runs/<branch-slug>/`:
|
|
79
|
+
|
|
80
|
+
| Artefato | Conteúdo |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `classification.json` | Watchdogs ativados por arquivo + status (A/M/D/R) |
|
|
83
|
+
| `candidates.json` | Indícios da pré-análise Jev (regra + arquivo + confiança) |
|
|
84
|
+
| `briefs/<watchdog>.md` | Instruções prontas para cada subagente |
|
|
85
|
+
| `manifest.json` | Resumo: watchdogs, arquivos, deletados, status, contexto, Jev |
|
|
86
|
+
|
|
87
|
+
**Excluídos automaticamente:** lock files, `dist/`, `build/`, `.env*`, imagens, source
|
|
88
|
+
maps (padrões do projeto em `.houndsman/exclusions.ts`).
|
|
89
|
+
**Deletados:** não entram como conteúdo; vão para o brief de watchdogs com
|
|
90
|
+
`breakingChange` como candidatos a **breaking change**.
|
|
91
|
+
|
|
92
|
+
### Pré-análise Jev (Passo 1.5, opcional)
|
|
93
|
+
|
|
94
|
+
Se `TYPESAFE_API_KEY` estiver definida, cada arquivo é avaliado pelo modelo **Jev** contra
|
|
95
|
+
as regras atômicas dos watchdogs. Respostas acima do limiar viram **candidatos** em
|
|
96
|
+
`candidates.json` e entram no brief como *warm start* — o subagente **confirma ou refuta**.
|
|
97
|
+
|
|
98
|
+
- **Não é achado**: candidatos não bloqueiam.
|
|
99
|
+
- **Falha-aberto**: sem chave / erro / timeout → `available:false`; a revisão segue idêntica.
|
|
100
|
+
- Desativar: `houndsman --no-jev`.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Passo 2 — Revisar (skill despacha os subagentes)
|
|
105
|
+
|
|
106
|
+
Um subagente por brief, todos no mesmo bloco de tool calls. O prompt é o próprio brief:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Leia e execute o brief: .houndsman/.runs/<branch-slug>/briefs/<watchdog-id>.md
|
|
110
|
+
Você é o watchdog <watchdog-id> do {{projectName}}.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Cada subagente grava `findings/<watchdog-id>.json` (schema `.houndsman/core/skill/OUTPUT-FORMAT.md`).
|
|
114
|
+
Sem achados → `"findings": []` (arquivo obrigatório).
|
|
115
|
+
|
|
116
|
+
**NUNCA enviar:** `git diff` completo · histórico de commits · arquivos de outros domínios ·
|
|
117
|
+
`AGENTS.md` completo (já está no contexto global).
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Passo 3 — Finalizar (skill executa)
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
houndsman --finalize
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Falha com mensagem clara se faltar algum `findings/<watchdog>.json`. Quando todos existem:
|
|
128
|
+
1. Valida o schema de cada JSON
|
|
129
|
+
2. Deduplica achados por `(file, line, rule)` mesclando watchdogs sobrepostos
|
|
130
|
+
3. Agrupa por severidade: ❌ blocking > ⚠️ attention > 💡 suggestion > 🔎 nit
|
|
131
|
+
4. Grava `.houndsman/.runs/<branch-slug>/review.md` + tabela de cobertura
|
|
132
|
+
5. Anexa os candidatos Jev não confirmados em `🔎 Candidatos não verificados` (não bloqueiam)
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Estrutura do Relatório
|
|
137
|
+
|
|
138
|
+
```markdown
|
|
139
|
+
# Review — [branch]
|
|
140
|
+
|
|
141
|
+
**Data:** YYYY-MM-DD
|
|
142
|
+
**Watchdogs ativos:** [lista]
|
|
143
|
+
**Achados:** N bloqueantes · N atenção · N sugestões · N nits
|
|
144
|
+
|
|
145
|
+
## Contexto da PR
|
|
146
|
+
**PR:** #123 Título — url
|
|
147
|
+
**Branch:** `feature/x` → `main`
|
|
148
|
+
**Objetivo:** ...
|
|
149
|
+
**Resumo das alterações:**
|
|
150
|
+
- ...
|
|
151
|
+
|
|
152
|
+
## ❌ blocking
|
|
153
|
+
- **<regra>** — `arquivo:linha` · _<watchdogs>_
|
|
154
|
+
<mensagem>
|
|
155
|
+
**Ação:** <ação>
|
|
156
|
+
|
|
157
|
+
## Cobertura
|
|
158
|
+
| Watchdog | Arquivos revisados | Findings |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
|
|
161
|
+
## Conclusão
|
|
162
|
+
**Status:** ✅ Aprovado / ⚠️ Aprovado com Ressalvas / ❌ Mudanças Necessárias
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Fluxo Completo
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
houndsman context (gh) ──► context.json ──► subagente ──► context-summary.json
|
|
171
|
+
↓
|
|
172
|
+
git diff --name-status (base...head)
|
|
173
|
+
↓
|
|
174
|
+
[1] houndsman ──► classification.json + candidates.json + briefs/ + manifest.json
|
|
175
|
+
↓
|
|
176
|
+
[2] N subagentes em paralelo ──► findings/<watchdog>.json
|
|
177
|
+
↓
|
|
178
|
+
[3] houndsman --finalize ──► review.md
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Invocação
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
/houndsman
|
|
187
|
+
/houndsman feature/add-webhook-retry
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Se a branch não for informada, detecta com `git branch --show-current`. Base do diff: `main`.
|
|
191
|
+
|
|
192
|
+
**Comandos do CLI:**
|
|
193
|
+
|
|
194
|
+
| Comando | Função |
|
|
195
|
+
|---|---|
|
|
196
|
+
| `houndsman context` | Passo 0 — contexto da PR via `gh` |
|
|
197
|
+
| `houndsman` | Passo 1 — preparar (briefs + manifesto) |
|
|
198
|
+
| `houndsman --finalize` | Passo 3 — gerar o relatório |
|
|
199
|
+
| `houndsman watchdog <id>` | Contexto de um watchdog (`--json`, `--rules`) |
|
|
200
|
+
| `houndsman setup` | Cria a configuração inicial do projeto |
|
|
201
|
+
| `houndsman update` | Sincroniza os arquivos auto-gerados (`.houndsman/core`) |
|
|
202
|
+
| `houndsman doctor` | Valida a configuração e o ambiente (anti-drift) |
|
|
203
|
+
| `houndsman test` | Suíte de testes da lib |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Anti-padrões
|
|
208
|
+
|
|
209
|
+
- ❌ Enviar git diff completo para qualquer watchdog
|
|
210
|
+
- ❌ Ativar watchdogs sem arquivos no seu domínio
|
|
211
|
+
- ❌ Carregar documentação que o watchdog não precisa
|
|
212
|
+
- ❌ Revisar arquivos gerados (lock files, builds, migrations)
|
|
213
|
+
- ❌ Escrever markdown livre em vez do JSON de `OUTPUT-FORMAT.md`
|
|
214
|
+
- ❌ Revisar arquivos `deleted` (não têm conteúdo) em vez de tratá-los como breaking change
|
|
215
|
+
- ❌ Comentários genéricos ("código bom", "pode melhorar")
|
|
216
|
+
- ❌ Sugestões de estilo triviais
|
|
217
|
+
- ❌ Reescrever código completo — apenas apontar direção
|
|
218
|
+
- ❌ Inventar objetivo/motivação da PR — o resumo vem **só** do `gh`; sem `gh`, omitir a seção
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Manutenção das Regras
|
|
223
|
+
|
|
224
|
+
> Alterar qualquer regra (adicionar, ajustar severidade, remover, corrigir escopo) → usar o
|
|
225
|
+
> skill **`houndsman-update`** (`src/skill/houndsman-update.md`).
|
|
226
|
+
> Regra de ouro: só é regra o que o projeto **aplica hoje** — prove com evidência no código
|
|
227
|
+
> (regra negativa com 0 ocorrências = seguida → **manter**).
|
|
228
|
+
> Após qualquer alteração, rodar `houndsman doctor` (anti-drift) + `houndsman test` + `ci:fix` + `ci:check`.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Performance Esperada
|
|
233
|
+
|
|
234
|
+
| Tamanho do PR | Watchdogs | Tempo |
|
|
235
|
+
|---|---|---|
|
|
236
|
+
| Pequeno (500 linhas) | 2-4 | ~30-60s |
|
|
237
|
+
| Médio (5k linhas) | 4-7 | ~1-2 min |
|
|
238
|
+
| Grande (50k linhas) | 7-10 | ~3-5 min |
|
|
239
|
+
| Enorme (300k linhas) | 10-15 | ~5-8 min |
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Project fake" usado pela suíte de testes da lib.
|
|
3
|
+
*
|
|
4
|
+
* Os testes validam o MOTOR (`core/`), não as regras de um projeto real —
|
|
5
|
+
* por isso usam esta configuração fictícia em vez da config do projeto real.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineHoundsmanConfig, type HoundsmanConfig } from "../../core/config";
|
|
9
|
+
import { defineWatchdog } from "../../core/types";
|
|
10
|
+
|
|
11
|
+
/** Watchdog A — regra bloqueante com spec Jev + outras severidades. */
|
|
12
|
+
export const alphaWatchdog = defineWatchdog({
|
|
13
|
+
id: "alpha",
|
|
14
|
+
name: "Alpha",
|
|
15
|
+
description: "Watchdog A do projeto fake.",
|
|
16
|
+
scope: "src/alpha/**",
|
|
17
|
+
classify: [{ value: "^src/alpha/" }],
|
|
18
|
+
rules: [
|
|
19
|
+
{ severity: "blocking", text: "Nunca usar `any`.", jev: { kind: "noul" } },
|
|
20
|
+
{ severity: "attention", text: "Validar entrada do usuário." },
|
|
21
|
+
{ severity: "suggestion", text: "Evitar console.log." },
|
|
22
|
+
],
|
|
23
|
+
contextFiles: ["README.md"],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** Watchdog B — cobre testes e um padrão de nome de arquivo. */
|
|
27
|
+
export const betaWatchdog = defineWatchdog({
|
|
28
|
+
id: "beta",
|
|
29
|
+
name: "Beta",
|
|
30
|
+
description: "Watchdog B do projeto fake.",
|
|
31
|
+
scope: "src/beta/**, arquivos de teste e o arquivo especial",
|
|
32
|
+
classify: [
|
|
33
|
+
{ value: "^src/beta/" },
|
|
34
|
+
{ value: "\\.test\\.ts$" },
|
|
35
|
+
{ value: "special\\.ts$" },
|
|
36
|
+
],
|
|
37
|
+
rules: [
|
|
38
|
+
{
|
|
39
|
+
severity: "blocking",
|
|
40
|
+
text: "Nunca commitar segredos.",
|
|
41
|
+
jev: { kind: "noul", instructions: "O estado contém segredo hardcoded?" },
|
|
42
|
+
},
|
|
43
|
+
{ severity: "attention", text: "Evitar código morto." },
|
|
44
|
+
],
|
|
45
|
+
contextFiles: ["README.md"],
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const FAKE_WATCHDOG_DEFINITIONS = {
|
|
49
|
+
alpha: alphaWatchdog,
|
|
50
|
+
beta: betaWatchdog,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type FakeWatchdogId = keyof typeof FAKE_WATCHDOG_DEFINITIONS;
|
|
54
|
+
|
|
55
|
+
/** Configuração completa do projeto fake. */
|
|
56
|
+
export const fakeHoundsmanConfig: HoundsmanConfig<FakeWatchdogId> =
|
|
57
|
+
defineHoundsmanConfig<FakeWatchdogId>({
|
|
58
|
+
projectName: "Fake Project",
|
|
59
|
+
artifactDir: "tmp/fake-review",
|
|
60
|
+
watchdogs: FAKE_WATCHDOG_DEFINITIONS,
|
|
61
|
+
exclusions: [/^dist\//, /\.lock$/],
|
|
62
|
+
docMappings: [[/^src\//, ["README.md"]]],
|
|
63
|
+
alwaysIncludeDocs: ["README.md"],
|
|
64
|
+
});
|