@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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -3
  3. package/assets/houndsman.png +0 -0
  4. package/package.json +63 -5
  5. package/src/cli/index.ts +210 -0
  6. package/src/core/assets.ts +20 -0
  7. package/src/core/brief.ts +162 -0
  8. package/src/core/commands/context.ts +39 -0
  9. package/src/core/commands/review.ts +50 -0
  10. package/src/core/commands/scaffold.ts +373 -0
  11. package/src/core/commands/setup.ts +75 -0
  12. package/src/core/commands/test.ts +20 -0
  13. package/src/core/commands/update.ts +30 -0
  14. package/src/core/commands/watchdog.ts +105 -0
  15. package/src/core/config-loader.ts +87 -0
  16. package/src/core/config.ts +59 -0
  17. package/src/core/doc-discoverer.ts +33 -0
  18. package/src/core/file-classifier.ts +247 -0
  19. package/src/core/jev/index.ts +12 -0
  20. package/src/core/jev/jev-candidates.ts +105 -0
  21. package/src/core/jev/jev-client.ts +99 -0
  22. package/src/core/jev/jev-detector.ts +203 -0
  23. package/src/core/jev/jev-questions.ts +92 -0
  24. package/src/core/jev/jev-types.ts +91 -0
  25. package/src/core/orchestrator.ts +444 -0
  26. package/src/core/paths.ts +30 -0
  27. package/src/core/pr-context.ts +271 -0
  28. package/src/core/registry-check.ts +145 -0
  29. package/src/core/report-builder.ts +357 -0
  30. package/src/core/types.ts +137 -0
  31. package/src/core/watchdog-registry.ts +43 -0
  32. package/src/index.ts +56 -0
  33. package/src/skill/AUDIT-PROTOCOL.md +96 -0
  34. package/src/skill/OUTPUT-FORMAT.md +70 -0
  35. package/src/skill/README.md +116 -0
  36. package/src/skill/SETUP.md +99 -0
  37. package/src/skill/example-review.md +58 -0
  38. package/src/skill/houndsman-update.md +251 -0
  39. package/src/skill/houndsman.md +239 -0
  40. package/src/tests/fixtures/fake-project.ts +64 -0
  41. 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
+ });