@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.
Files changed (61) hide show
  1. package/README.md +68 -89
  2. package/assets/logo-legacy.png +0 -0
  3. package/assets/logo.png +0 -0
  4. package/package.json +3 -2
  5. package/src/cli/index.ts +101 -10
  6. package/src/core/assets.ts +11 -0
  7. package/src/core/brief.ts +27 -5
  8. package/src/core/commands/findings.ts +618 -0
  9. package/src/core/commands/review.ts +2 -0
  10. package/src/core/commands/scaffold.ts +33 -44
  11. package/src/core/config.ts +42 -0
  12. package/src/core/diff-source.ts +140 -0
  13. package/src/core/file-classifier.ts +50 -2
  14. package/src/core/finding-anchor.ts +147 -0
  15. package/src/core/findings-state.ts +556 -0
  16. package/src/core/fix-batch.ts +236 -0
  17. package/src/core/git-revision.ts +105 -0
  18. package/src/core/jev/index.ts +1 -0
  19. package/src/core/jev/jev-client.ts +72 -11
  20. package/src/core/jev/jev-detector.ts +21 -19
  21. package/src/core/jev/jev-verify.ts +178 -0
  22. package/src/core/orchestrator.ts +362 -23
  23. package/src/core/paths.ts +9 -0
  24. package/src/core/registry-check.ts +15 -0
  25. package/src/core/report-builder.ts +118 -9
  26. package/src/core/run-status.ts +123 -0
  27. package/src/core/types.ts +10 -0
  28. package/src/core/verify.ts +175 -0
  29. package/src/core/watchdog-registry.ts +1 -2
  30. package/src/index.ts +78 -2
  31. package/src/server/artifacts.ts +67 -0
  32. package/src/server/client/App.tsx +158 -0
  33. package/src/server/client/ArtifactsPanel.tsx +48 -0
  34. package/src/server/client/Filters.tsx +83 -0
  35. package/src/server/client/FindingCard.tsx +122 -0
  36. package/src/server/client/FindingsList.tsx +54 -0
  37. package/src/server/client/RunList.tsx +52 -0
  38. package/src/server/client/SummaryBar.tsx +21 -0
  39. package/src/server/client/api.ts +37 -0
  40. package/src/server/client/constants.ts +94 -0
  41. package/src/server/client/index.tsx +12 -0
  42. package/src/server/client/types.ts +52 -0
  43. package/src/server/client-build.ts +47 -0
  44. package/src/server/index.ts +376 -0
  45. package/src/skill/AUDIT-PROTOCOL.md +2 -2
  46. package/src/skill/README.md +43 -11
  47. package/src/skill/SETUP.md +16 -4
  48. package/src/skill/SKILL.md +48 -0
  49. package/src/skill/fix.md +206 -0
  50. package/src/skill/{houndsman.md → hunt.md} +83 -33
  51. package/src/skill/{houndsman-update.md → train.md} +12 -12
  52. package/src/skill/verify.md +70 -0
  53. package/src/tests/findings-anchor.test.ts +120 -0
  54. package/src/tests/findings-commands.test.ts +476 -0
  55. package/src/tests/findings-state.test.ts +325 -0
  56. package/src/tests/fix-batch.test.ts +257 -0
  57. package/src/tests/review-scripts.test.ts +299 -0
  58. package/src/tests/run-status.test.ts +147 -0
  59. package/src/tests/scaffold.test.ts +59 -0
  60. package/src/tests/server.test.ts +242 -0
  61. 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, ambos **falha-aberto**: **`gh`** (contexto da PR) e **`TYPESAFE_API_KEY`**
29
- (pré-análise Jev). Sem eles, a revisão roda igual.
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
- > **Não existe "modo completo".** O `setup` já cria a camada inicial com um watchdog de
58
- > exemplo — troque o exemplo pelas regras reais do seu projeto (ou use `/houndsman-update`).
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
- └─ Passo 3 consolida em review.md
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
- - Revisão normal: só `/houndsman` (a branch é detectada; base default `main`).
94
- - Revisar outra branch: `/houndsman feature/minha-branch`.
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 o skill **`/houndsman-update`**. Ele prova no código que o padrão
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
- - **`docs/CONTRIBUTING.md`** — arquitetura, fluxos internos, princípios e desenvolvimento da
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/houndsman.md` — orquestração do skill `/houndsman`.
195
- - `src/skill/houndsman-update.md` — manutenção das regras dos watchdogs.
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
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payconductor/houndsman",
3
- "version": "0.1.0",
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 e o ambiente (anti-drift)
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 e o ambiente (anti-drift)
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 (anti-drift). */
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
- console.log(
114
- ` ${process.env.TYPESAFE_API_KEY ? "✅" : "⚠️ "} TYPESAFE_API_KEY (opcional — pré-análise Jev)`,
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;
@@ -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 era
5
- * protocolo específico vive nas regras do watchdog (`rules`).
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
- return `- \`${file}\` (${status})`;
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
  }