@payconductor/houndsman 0.6.0 → 0.8.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 (71) hide show
  1. package/README.md +20 -0
  2. package/package.json +1 -1
  3. package/src/cli/commands/command.ts +10 -1
  4. package/src/cli/commands/context-command.ts +40 -2
  5. package/src/cli/commands/hunt-command.ts +28 -2
  6. package/src/cli/commands/review-command.ts +30 -2
  7. package/src/cli/commands/server-command.ts +7 -1
  8. package/src/cli/commands/worktree-command.ts +118 -0
  9. package/src/cli/help.ts +13 -3
  10. package/src/cli/index.ts +14 -4
  11. package/src/core/ast/service.ts +7 -2
  12. package/src/core/brief.ts +1 -0
  13. package/src/core/commands/context.ts +9 -1
  14. package/src/core/commands/findings.ts +17 -6
  15. package/src/core/commands/review.ts +13 -0
  16. package/src/core/commands/scaffold.ts +4 -2
  17. package/src/core/config.ts +13 -0
  18. package/src/core/defaults.ts +8 -1
  19. package/src/core/engine/houndsman-engine.ts +26 -3
  20. package/src/core/engine/review-scope.ts +17 -22
  21. package/src/core/engine/review-session.ts +25 -2
  22. package/src/core/engine/review-workspace.ts +204 -0
  23. package/src/core/git-runner.ts +25 -13
  24. package/src/core/github-review.ts +10 -7
  25. package/src/core/jev/jev-detector.ts +10 -2
  26. package/src/core/orchestrator.ts +62 -13
  27. package/src/core/project-config.ts +12 -0
  28. package/src/core/services/gh-client.ts +38 -22
  29. package/src/core/services/git-repository.ts +46 -0
  30. package/src/core/services/pr-resolver.ts +147 -0
  31. package/src/core/services/worktree-manager.ts +269 -0
  32. package/src/core/storage/file/file-storage.ts +14 -1
  33. package/src/core/storage/paths.ts +30 -1
  34. package/src/core/storage/sqlite/mappers.ts +4 -0
  35. package/src/core/storage/sqlite/schema.ts +13 -3
  36. package/src/core/storage/sqlite/sqlite-storage.ts +28 -8
  37. package/src/core/storage/types.ts +12 -0
  38. package/src/core/storage/work-area.ts +20 -1
  39. package/src/core/types.ts +2 -0
  40. package/src/harness/hunt/hunt-options.ts +17 -0
  41. package/src/harness/hunt/hunt-session.ts +7 -20
  42. package/src/index.ts +31 -0
  43. package/src/server/client/App.tsx +86 -22
  44. package/src/server/client/CommentsPanel.tsx +255 -0
  45. package/src/server/client/DeleteRunModal.tsx +163 -0
  46. package/src/server/client/FindingCard.tsx +12 -4
  47. package/src/server/client/FindingsList.tsx +4 -1
  48. package/src/server/client/ResumePanel.tsx +8 -8
  49. package/src/server/client/Sidebar.tsx +452 -0
  50. package/src/server/client/SubmitModal.tsx +61 -5
  51. package/src/server/client/icons.tsx +29 -0
  52. package/src/server/client/index.tsx +1 -1
  53. package/src/server/client/utils/constants.ts +2 -0
  54. package/src/server/client/utils/format.ts +47 -2
  55. package/src/server/client/utils/types.ts +108 -0
  56. package/src/server/editor.ts +129 -0
  57. package/src/server/index.ts +205 -3
  58. package/src/server/pr-comments.ts +256 -0
  59. package/src/server/pr-inbox.ts +213 -0
  60. package/src/server/run-repository.ts +136 -3
  61. package/src/skill/README.md +11 -3
  62. package/src/skill/SKILL.md +44 -10
  63. package/src/skill/hunt.md +33 -7
  64. package/src/tests/__snapshots__/baseline.test.ts.snap +94 -24
  65. package/src/tests/ai-core.test.ts +2 -2
  66. package/src/tests/cli.test.ts +35 -1
  67. package/src/tests/github-review.test.ts +9 -0
  68. package/src/tests/server-editor.test.ts +100 -0
  69. package/src/tests/server.test.ts +333 -2
  70. package/src/tests/worktree.test.ts +369 -0
  71. package/src/server/client/RunList.tsx +0 -75
@@ -39,13 +39,20 @@ Um único skill (`/houndsman`), quatro capabilities (detalhadas em `hunt.md`, `t
39
39
  | 2. Revisar | *(subagentes)* | skill | `findings/<watchdog>.json` (um por watchdog) |
40
40
  | 3. Finalizar | `bun run houndsman --finalize` | skill | `review.md` |
41
41
 
42
+ > **Alvo = PR** (ex.: “revise a PR 456”): troque os passos por `houndsman context --pr 456`,
43
+ > `houndsman --pr 456` e `houndsman --pr 456 --finalize`. O prepare cria um **worktree**
44
+ > detached em `<repo>/.houndsman/worktrees/<repo>/<branch>`; os briefs trazem
45
+ > `**Worktree:**` e os subagentes devem ler essa árvore (nunca o clone principal).
46
+
42
47
  ## Comandos
43
48
 
44
49
  | Comando | Função |
45
50
  |---|---|
46
- | `houndsman context` | Passo 0 — contexto da PR via `gh` |
51
+ | `houndsman context` | Passo 0 — contexto da PR via `gh` (`--pr <n>`/`--worktree <path>`) |
47
52
  | `houndsman` | Passo 1 — preparar (gera os briefs) |
48
- | `houndsman --finalize` | Passo 3 — gerar o relatório |
53
+ | `houndsman --pr <n>` | Passo 1 em worktree dedicado (revisões paralelas) |
54
+ | `houndsman --finalize` | Passo 3 — gerar o relatório (`--pr <n>` reusa o worktree) |
55
+ | `houndsman worktree <ação>` | `list`/`add`/`remove`/`prune`/`path` dos worktrees |
49
56
  | `houndsman watchdog <id>` | Contexto de um watchdog (`--json`, `--rules`) |
50
57
  | `houndsman findings <sub>` | Triagem/correção dos findings (`list`/`set`/`done`/`sync`/`batch`/`verify`/...) |
51
58
  | `houndsman server` | UI local de triagem (Bun.serve + Preact) |
@@ -57,7 +64,8 @@ Um único skill (`/houndsman`), quatro capabilities (detalhadas em `hunt.md`, `t
57
64
  ### Flags
58
65
 
59
66
  `--config <path>` · `--base <branch>` (default `main`) · `--head <ref>` (default `HEAD`) ·
60
- `--branch <nome>` · `--clean` · `--full` · `--no-jev` · `--fail-on-blocking` (no `--finalize`).
67
+ `--branch <nome>` · `--pr <n>` · `--worktree <path>` · `--clean` · `--full` · `--no-jev` ·
68
+ `--fail-on-blocking` (no `--finalize`).
61
69
 
62
70
  ## Artefatos
63
71
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: houndsman
3
- description: 'Revisa Pull Requests com watchdogs especializados por domínio de negócio e mantém essas regras alinhadas ao projeto. Use para revisar/code review de PR, atualizar as regras de revisão dos watchdogs e corrigir/verificar os findings da revisão. Capabilities: `hunt` (revisar a PR), `train` (atualizar regras), `fix` (corrigir achados), `verify` (violações resolvidas?).'
4
- argument-hint: 'Capability: hunt | train | fix | verify (default: hunt)'
3
+ description: 'Revisa Pull Requests com watchdogs especializados por domínio de negócio e mantém essas regras alinhadas ao projeto. Use para revisar/code review de PR, atualizar as regras de revisão dos watchdogs e corrigir/verificar os findings da revisão. Quando o alvo é uma PR (#456, “PR 456” ou URL do GitHub), revisa em worktree isolado automaticamente. Capabilities: `hunt` (revisar a PR), `train` (atualizar regras), `fix` (corrigir achados), `verify` (violações resolvidas?).'
4
+ argument-hint: 'Capability e alvo: hunt --pr 456 | hunt feature/x | train | fix | verify (default: hunt)'
5
5
  ---
6
6
 
7
7
  # houndsman — {{projectName}}
@@ -26,16 +26,50 @@ especialista por domínio · **finding** = um achado da revisão · **Jev** = pr
26
26
  ## Invocação
27
27
 
28
28
  ```
29
- /houndsman → hunt (default)
30
- /houndsman hunt → revisar a Pull Request
31
- /houndsman train → atualizar as regras dos watchdogs
32
- /houndsman fix → corrigir os findings
33
- /houndsman verify → conferir se as violações foram resolvidas
29
+ /houndsman → hunt (default)
30
+ /houndsman hunt → revisar a Pull Request
31
+ /houndsman hunt, revise a pr 456 → hunt da PR 456 (worktree automático)
32
+ /houndsman hunt feature/minha-branch → hunt da branch (clone atual)
33
+ /houndsman train → atualizar as regras dos watchdogs
34
+ /houndsman fix → corrigir os findings
35
+ /houndsman verify → conferir se as violações foram resolvidas
34
36
  ```
35
37
 
36
- Um argumento extra é o alvo da capability (ex.: `/houndsman hunt feature/minha-branch`).
37
- Escolha a capability pelo que o usuário quer, leia o documento correspondente em
38
- `.houndsman/core/skill/<capability>.md` e siga-o.
38
+ Um argumento extra é o alvo da capability. Escolha a capability pelo que o usuário quer,
39
+ leia o documento correspondente em `.houndsman/core/skill/<capability>.md` e siga-o.
40
+
41
+ ### Alvo da revisão (PR × branch)
42
+
43
+ Interprete o alvo em linguagem natural **antes** de rodar o CLI:
44
+
45
+ | O usuário disse | Alvo | Flags |
46
+ |---|---|---|
47
+ | `PR 456`, `#456`, `pull 456`, `456` (número sozinho), `https://github.com/org/repo/pull/456` | PR | `--pr 456` |
48
+ | `feature/minha-branch`, `branch feature/x` | branch | `--branch feature/minha-branch` |
49
+ | nada | branch atual | *(sem flag)* |
50
+
51
+ > **PR citada ⇒ worktree obrigatório.** Nunca rode `houndsman` “puro” para revisar uma PR que o
52
+ > usuário nomeou: o prepare precisa criar o worktree dedicado. Também **nunca** faça
53
+ > `git checkout` da PR no clone principal — é isso que permite revisar várias PRs ao mesmo
54
+ > tempo.
55
+
56
+ Sequência para uma PR (ex.: 456):
57
+
58
+ ```bash
59
+ houndsman context --pr 456
60
+ houndsman --pr 456 # cria/reusa o worktree e imprime o caminho
61
+ # despache os briefs (cada um traz **Worktree:** <path>)
62
+ houndsman --pr 456 --finalize
63
+ ```
64
+
65
+ - O worktree vive em `<repo>/.houndsman/worktrees/<repo>/<branch>` (detached no commit da PR)
66
+ e é reutilizado nas próximas execuções da mesma branch. Ele fica **dentro do workspace**
67
+ (e o staging também) para o agente ler/executar sem prompts de permissão.
68
+ - Use `--worktree <path>` para aproveitar um worktree já existente.
69
+ - O prepare imprime `PR #456 — <head> → <base>`; use `<head>` em `status ping`,
70
+ `findings` e `verify` (`--branch <head>`). A review guarda o worktree, então esses
71
+ comandos leem a árvore correta.
72
+ - PR de fork também funciona: o CLI busca `refs/pull/<n>/head`.
39
73
 
40
74
  > **Depois de revisar (`hunt`), valide na UI:** `bun run houndsman server --open` — revise os
41
75
  > achados e marque os que devem ser executados (`execute`) antes de rodar `fix`.
package/src/skill/hunt.md CHANGED
@@ -4,16 +4,32 @@
4
4
  > (`.agents/skills/houndsman/SKILL.md` e `.claude/skills/houndsman/SKILL.md`).
5
5
 
6
6
  Revisão de PR: classificação de arquivos → revisão paralela por domínio → consolidação.
7
- Invocação: `/houndsman` (default) ou `/houndsman hunt`. O motor é o CLI **`houndsman`**;
8
- as regras do projeto ficam em `houndsman.config.ts` + `.houndsman/`.
7
+ Invocação: `/houndsman` (default), `/houndsman hunt` ou `/houndsman hunt pr 456`.
8
+ O motor é o CLI **`houndsman`**; as regras do projeto ficam em `houndsman.config.ts` +
9
+ `.houndsman/`.
10
+
11
+ ## Alvo da revisão
12
+
13
+ Traduza o pedido do usuário para flags **antes** de começar:
14
+
15
+ | Pedido | Alvo | Flag |
16
+ |---|---|---|
17
+ | “revise a PR 456”, “#456”, URL `.../pull/456` | PR | `--pr 456` |
18
+ | “revise a branch feature/x” | branch | `--branch feature/x` |
19
+ | “revise esta PR / o que mudou” | branch atual | *(sem flag)* |
20
+
21
+ > **Se o alvo é uma PR, use worktree.** Rode `houndsman --pr 456` (não `houndsman`), nunca faça
22
+ > checkout no clone principal e use o caminho impresso na saída como raiz dos subagentes.
23
+ > Isso permite revisar outras PRs em paralelo, cada uma na sua árvore.
9
24
 
10
25
  **Quando usar:** revisar PR antes de merge · code review de feature branch · validar
11
26
  mudanças antes de push.
12
27
 
13
28
  > **Harness nativo (opcional):** além deste skill, o CLI tem `houndsman hunt` — o mesmo
14
29
  > fluxo executado pelo próprio houndsman, com tools read-only, investigação por evidência
15
- > e `VERIFY` obrigatório. Use `houndsman hunt --dry-run` para inspecionar sem IA. Detalhes
16
- > em `docs/HARNESS.md`.
30
+ > e `VERIFY` obrigatório. Use `houndsman hunt --dry-run` para inspecionar sem IA. Para uma
31
+ > PR específica, `houndsman hunt --pr 456` roda no worktree automaticamente. Detalhes em
32
+ > `docs/HARNESS.md`.
17
33
 
18
34
  ## Quem executa o quê
19
35
 
@@ -56,7 +72,8 @@ O contexto vem da **descrição da PR** via GitHub CLI (`gh`), quando disponíve
56
72
  Um subagente executa o CLI e resume:
57
73
 
58
74
  ```
59
- Você é o subagente de contexto do houndsman. Execute `houndsman context`;
75
+ Você é o subagente de contexto do houndsman. Execute `houndsman context --pr 456`
76
+ (ou `houndsman context --worktree <path>` se o prepare já criou o worktree);
60
77
  leia o `context-brief.md` indicado na saída; siga as instruções e grave
61
78
  `context-summary.json`. Retorne `objective`/`motivation`/`changeSummary` ou "indisponível".
62
79
  ```
@@ -72,9 +89,16 @@ leia o `context-brief.md` indicado na saída; siga as instruções e grave
72
89
  ## Passo 1 — Preparar (skill executa)
73
90
 
74
91
  ```bash
75
- houndsman --base main
92
+ houndsman --pr 456 # alvo = PR (cria/reusa o worktree)
93
+ houndsman --base main # alvo = branch atual (clone principal)
76
94
  ```
77
95
 
96
+ Quando o alvo é uma PR, o prepare consulta a PR via `gh`, busca `refs/pull/<n>/head`, cria o
97
+ worktree **detached** em `<repo>/.houndsman/worktrees/<repo>/<branch>` e imprime o caminho.
98
+ Cada brief passa a trazer `**Worktree:** <path>` — essa é a raiz que os subagentes devem ler.
99
+ Guarde também a linha `PR #456 — <head> → <base>`: `<head>` é a branch a usar em
100
+ `status ping`, `findings` e `verify`.
101
+
78
102
  Grava os artefatos no **storage global** (SQLite em `<HOUNDSMAN_HOME>/houndsman.db`) e o
79
103
  staging dos subagentes em `<HOUNDSMAN_HOME>/work/<reviewId>/`:
80
104
 
@@ -157,6 +181,7 @@ calls) — evita estourar limites de taxa quando há muitos watchdogs. O prompt
157
181
  ```
158
182
  Leia e execute o brief: <caminho absoluto impresso pelo prepare>/briefs/<watchdog-id>.md
159
183
  Você é o watchdog <watchdog-id> do {{projectName}}.
184
+ Use como raiz de leitura o **Worktree:** indicado no brief (nunca o clone principal).
160
185
  ```
161
186
 
162
187
  Cada subagente grava o `outputPath` indicado no brief (schema `.houndsman/core/skill/OUTPUT-FORMAT.md`).
@@ -171,7 +196,8 @@ não são lidos: registre-os em `skipped`.
171
196
  ## Passo 3 — Finalizar (skill executa)
172
197
 
173
198
  ```bash
174
- houndsman --finalize
199
+ houndsman --pr 456 --finalize # alvo = PR (reusa o worktree gravado)
200
+ houndsman --finalize # alvo = branch atual
175
201
  ```
176
202
 
177
203
  Falha com mensagem clara se faltar algum `findings/<watchdog>.json` no staging. Quando todos existem:
@@ -200,8 +200,8 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 2
200
200
  ".agents/skills/houndsman/SKILL.md":
201
201
  "---
202
202
  name: houndsman
203
- description: 'Revisa Pull Requests com watchdogs especializados por domínio de negócio e mantém essas regras alinhadas ao projeto. Use para revisar/code review de PR, atualizar as regras de revisão dos watchdogs e corrigir/verificar os findings da revisão. Capabilities: \`hunt\` (revisar a PR), \`train\` (atualizar regras), \`fix\` (corrigir achados), \`verify\` (violações resolvidas?).'
204
- argument-hint: 'Capability: hunt | train | fix | verify (default: hunt)'
203
+ description: 'Revisa Pull Requests com watchdogs especializados por domínio de negócio e mantém essas regras alinhadas ao projeto. Use para revisar/code review de PR, atualizar as regras de revisão dos watchdogs e corrigir/verificar os findings da revisão. Quando o alvo é uma PR (#456, “PR 456” ou URL do GitHub), revisa em worktree isolado automaticamente. Capabilities: \`hunt\` (revisar a PR), \`train\` (atualizar regras), \`fix\` (corrigir achados), \`verify\` (violações resolvidas?).'
204
+ argument-hint: 'Capability e alvo: hunt --pr 456 | hunt feature/x | train | fix | verify (default: hunt)'
205
205
  ---
206
206
 
207
207
  # houndsman — acme
@@ -226,16 +226,50 @@ especialista por domínio · **finding** = um achado da revisão · **Jev** = pr
226
226
  ## Invocação
227
227
 
228
228
  \`\`\`
229
- /houndsman → hunt (default)
230
- /houndsman hunt → revisar a Pull Request
231
- /houndsman train → atualizar as regras dos watchdogs
232
- /houndsman fix → corrigir os findings
233
- /houndsman verify → conferir se as violações foram resolvidas
229
+ /houndsman → hunt (default)
230
+ /houndsman hunt → revisar a Pull Request
231
+ /houndsman hunt, revise a pr 456 → hunt da PR 456 (worktree automático)
232
+ /houndsman hunt feature/minha-branch → hunt da branch (clone atual)
233
+ /houndsman train → atualizar as regras dos watchdogs
234
+ /houndsman fix → corrigir os findings
235
+ /houndsman verify → conferir se as violações foram resolvidas
234
236
  \`\`\`
235
237
 
236
- Um argumento extra é o alvo da capability (ex.: \`/houndsman hunt feature/minha-branch\`).
237
- Escolha a capability pelo que o usuário quer, leia o documento correspondente em
238
- \`.houndsman/core/skill/<capability>.md\` e siga-o.
238
+ Um argumento extra é o alvo da capability. Escolha a capability pelo que o usuário quer,
239
+ leia o documento correspondente em \`.houndsman/core/skill/<capability>.md\` e siga-o.
240
+
241
+ ### Alvo da revisão (PR × branch)
242
+
243
+ Interprete o alvo em linguagem natural **antes** de rodar o CLI:
244
+
245
+ | O usuário disse | Alvo | Flags |
246
+ |---|---|---|
247
+ | \`PR 456\`, \`#456\`, \`pull 456\`, \`456\` (número sozinho), \`https://github.com/org/repo/pull/456\` | PR | \`--pr 456\` |
248
+ | \`feature/minha-branch\`, \`branch feature/x\` | branch | \`--branch feature/minha-branch\` |
249
+ | nada | branch atual | *(sem flag)* |
250
+
251
+ > **PR citada ⇒ worktree obrigatório.** Nunca rode \`houndsman\` “puro” para revisar uma PR que o
252
+ > usuário nomeou: o prepare precisa criar o worktree dedicado. Também **nunca** faça
253
+ > \`git checkout\` da PR no clone principal — é isso que permite revisar várias PRs ao mesmo
254
+ > tempo.
255
+
256
+ Sequência para uma PR (ex.: 456):
257
+
258
+ \`\`\`bash
259
+ houndsman context --pr 456
260
+ houndsman --pr 456 # cria/reusa o worktree e imprime o caminho
261
+ # despache os briefs (cada um traz **Worktree:** <path>)
262
+ houndsman --pr 456 --finalize
263
+ \`\`\`
264
+
265
+ - O worktree vive em \`<repo>/.houndsman/worktrees/<repo>/<branch>\` (detached no commit da PR)
266
+ e é reutilizado nas próximas execuções da mesma branch. Ele fica **dentro do workspace**
267
+ (e o staging também) para o agente ler/executar sem prompts de permissão.
268
+ - Use \`--worktree <path>\` para aproveitar um worktree já existente.
269
+ - O prepare imprime \`PR #456 — <head> → <base>\`; use \`<head>\` em \`status ping\`,
270
+ \`findings\` e \`verify\` (\`--branch <head>\`). A review guarda o worktree, então esses
271
+ comandos leem a árvore correta.
272
+ - PR de fork também funciona: o CLI busca \`refs/pull/<n>/head\`.
239
273
 
240
274
  > **Depois de revisar (\`hunt\`), valide na UI:** \`bun run houndsman server --open\` — revise os
241
275
  > achados e marque os que devem ser executados (\`execute\`) antes de rodar \`fix\`.
@@ -251,8 +285,8 @@ Escolha a capability pelo que o usuário quer, leia o documento correspondente e
251
285
  ".claude/skills/houndsman/SKILL.md":
252
286
  "---
253
287
  name: houndsman
254
- description: 'Revisa Pull Requests com watchdogs especializados por domínio de negócio e mantém essas regras alinhadas ao projeto. Use para revisar/code review de PR, atualizar as regras de revisão dos watchdogs e corrigir/verificar os findings da revisão. Capabilities: \`hunt\` (revisar a PR), \`train\` (atualizar regras), \`fix\` (corrigir achados), \`verify\` (violações resolvidas?).'
255
- argument-hint: 'Capability: hunt | train | fix | verify (default: hunt)'
288
+ description: 'Revisa Pull Requests com watchdogs especializados por domínio de negócio e mantém essas regras alinhadas ao projeto. Use para revisar/code review de PR, atualizar as regras de revisão dos watchdogs e corrigir/verificar os findings da revisão. Quando o alvo é uma PR (#456, “PR 456” ou URL do GitHub), revisa em worktree isolado automaticamente. Capabilities: \`hunt\` (revisar a PR), \`train\` (atualizar regras), \`fix\` (corrigir achados), \`verify\` (violações resolvidas?).'
289
+ argument-hint: 'Capability e alvo: hunt --pr 456 | hunt feature/x | train | fix | verify (default: hunt)'
256
290
  ---
257
291
 
258
292
  # houndsman — acme
@@ -277,16 +311,50 @@ especialista por domínio · **finding** = um achado da revisão · **Jev** = pr
277
311
  ## Invocação
278
312
 
279
313
  \`\`\`
280
- /houndsman → hunt (default)
281
- /houndsman hunt → revisar a Pull Request
282
- /houndsman train → atualizar as regras dos watchdogs
283
- /houndsman fix → corrigir os findings
284
- /houndsman verify → conferir se as violações foram resolvidas
314
+ /houndsman → hunt (default)
315
+ /houndsman hunt → revisar a Pull Request
316
+ /houndsman hunt, revise a pr 456 → hunt da PR 456 (worktree automático)
317
+ /houndsman hunt feature/minha-branch → hunt da branch (clone atual)
318
+ /houndsman train → atualizar as regras dos watchdogs
319
+ /houndsman fix → corrigir os findings
320
+ /houndsman verify → conferir se as violações foram resolvidas
321
+ \`\`\`
322
+
323
+ Um argumento extra é o alvo da capability. Escolha a capability pelo que o usuário quer,
324
+ leia o documento correspondente em \`.houndsman/core/skill/<capability>.md\` e siga-o.
325
+
326
+ ### Alvo da revisão (PR × branch)
327
+
328
+ Interprete o alvo em linguagem natural **antes** de rodar o CLI:
329
+
330
+ | O usuário disse | Alvo | Flags |
331
+ |---|---|---|
332
+ | \`PR 456\`, \`#456\`, \`pull 456\`, \`456\` (número sozinho), \`https://github.com/org/repo/pull/456\` | PR | \`--pr 456\` |
333
+ | \`feature/minha-branch\`, \`branch feature/x\` | branch | \`--branch feature/minha-branch\` |
334
+ | nada | branch atual | *(sem flag)* |
335
+
336
+ > **PR citada ⇒ worktree obrigatório.** Nunca rode \`houndsman\` “puro” para revisar uma PR que o
337
+ > usuário nomeou: o prepare precisa criar o worktree dedicado. Também **nunca** faça
338
+ > \`git checkout\` da PR no clone principal — é isso que permite revisar várias PRs ao mesmo
339
+ > tempo.
340
+
341
+ Sequência para uma PR (ex.: 456):
342
+
343
+ \`\`\`bash
344
+ houndsman context --pr 456
345
+ houndsman --pr 456 # cria/reusa o worktree e imprime o caminho
346
+ # despache os briefs (cada um traz **Worktree:** <path>)
347
+ houndsman --pr 456 --finalize
285
348
  \`\`\`
286
349
 
287
- Um argumento extra é o alvo da capability (ex.: \`/houndsman hunt feature/minha-branch\`).
288
- Escolha a capability pelo que o usuário quer, leia o documento correspondente em
289
- \`.houndsman/core/skill/<capability>.md\` e siga-o.
350
+ - O worktree vive em \`<repo>/.houndsman/worktrees/<repo>/<branch>\` (detached no commit da PR)
351
+ e é reutilizado nas próximas execuções da mesma branch. Ele fica **dentro do workspace**
352
+ (e o staging também) para o agente ler/executar sem prompts de permissão.
353
+ - Use \`--worktree <path>\` para aproveitar um worktree já existente.
354
+ - O prepare imprime \`PR #456 — <head> → <base>\`; use \`<head>\` em \`status ping\`,
355
+ \`findings\` e \`verify\` (\`--branch <head>\`). A review guarda o worktree, então esses
356
+ comandos leem a árvore correta.
357
+ - PR de fork também funciona: o CLI busca \`refs/pull/<n>/head\`.
290
358
 
291
359
  > **Depois de revisar (\`hunt\`), valide na UI:** \`bun run houndsman server --open\` — revise os
292
360
  > achados e marque os que devem ser executados (\`execute\`) antes de rodar \`fix\`.
@@ -300,10 +368,12 @@ Escolha a capability pelo que o usuário quer, leia o documento correspondente e
300
368
  "
301
369
  ,
302
370
  ".houndsman/.gitignore":
303
- "# Diretórios gerados pelo houndsman — \`core/\` é regenerado por \`houndsman update\`
304
- # e \`.runs/\` guarda os artefatos de revisão.
371
+ "# Diretórios gerados pelo houndsman — \`core/\` é regenerado por \`houndsman update\`.
372
+ # \`work/\` e \`worktrees/\` são runtime de revisão (staging e worktrees).
305
373
  core/
306
374
  .runs/
375
+ work/
376
+ worktrees/
307
377
  "
308
378
  ,
309
379
  ".houndsman/core/types.ts":
@@ -594,7 +664,7 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 3
594
664
  },
595
665
  {
596
666
  "file": ".houndsman/core/skill/README.md",
597
- "sha256": "0c1514669d044033",
667
+ "sha256": "1738ef9c6d9ec919",
598
668
  },
599
669
  {
600
670
  "file": ".houndsman/core/skill/SETUP.md",
@@ -610,7 +680,7 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 3
610
680
  },
611
681
  {
612
682
  "file": ".houndsman/core/skill/hunt.md",
613
- "sha256": "b55507d62d3c1fcd",
683
+ "sha256": "1cf68076325da4ab",
614
684
  },
615
685
  {
616
686
  "file": ".houndsman/core/skill/train.md",
@@ -16,8 +16,8 @@ const project = new ProjectConfig(fakeHoundsmanConfig);
16
16
 
17
17
  describe("config de IA", () => {
18
18
  test("modelo default do DeepSeek é 4.1 flash", () => {
19
- expect(DEFAULT_AI_MODEL).toBe("deepseek-v4.1-flash");
20
- expect(project.aiModel).toBe("deepseek-v4.1-flash");
19
+ expect(DEFAULT_AI_MODEL).toBe("deepseek-v4-pro");
20
+ expect(project.aiModel).toBe("deepseek-v4-pro");
21
21
  });
22
22
  });
23
23
 
@@ -152,10 +152,44 @@ describe("cli smoke — imports dinâmicos", () => {
152
152
  }
153
153
  });
154
154
 
155
+ test("findings submit filtra pela decisão (a executar)", () => {
156
+ const project = makeProject();
157
+ try {
158
+ const pending = runCli(project.cwd, [
159
+ "findings",
160
+ "submit",
161
+ "--dry-run",
162
+ "--json",
163
+ "--branch",
164
+ "main",
165
+ "--decision",
166
+ "pending",
167
+ ]);
168
+ expect(pending.code).toBe(0);
169
+ expect(JSON.parse(pending.stdout).totals.candidates).toBe(1);
170
+
171
+ const execute = runCli(project.cwd, [
172
+ "findings",
173
+ "submit",
174
+ "--dry-run",
175
+ "--json",
176
+ "--branch",
177
+ "main",
178
+ "--decision",
179
+ "execute",
180
+ ]);
181
+ expect(execute.code).toBe(0);
182
+ expect(JSON.parse(execute.stdout).totals.candidates).toBe(0);
183
+ } finally {
184
+ project.cleanup();
185
+ }
186
+ });
187
+
155
188
  test("doctor valida a config (import lazy de registry-check)", () => {
156
189
  const project = makeProject();
157
190
  try {
158
- const result = runCli(project.cwd, ["doctor"]);
191
+ // `--no-ping`: o teste cobre import lazy, não latência de rede.
192
+ const result = runCli(project.cwd, ["doctor", "--no-ping"]);
159
193
  expect(result.code).toBe(0);
160
194
  expect(result.stdout).toContain("Validando");
161
195
  } finally {
@@ -294,6 +294,15 @@ describe("selectSubmitCandidates", () => {
294
294
  expect(selectSubmitCandidates(state, { execStates: ["done"] })).toEqual([]);
295
295
  });
296
296
 
297
+ test("filtra pela decisão (a executar vs ainda pendente)", () => {
298
+ const execute = selectSubmitCandidates(state, { decisions: ["execute"] });
299
+ expect(execute.map((c) => c.id)).toEqual(["a"]);
300
+ expect(execute[0].decision).toBe("execute");
301
+ expect(
302
+ selectSubmitCandidates(state, { decisions: ["pending"] }).map((c) => c.id),
303
+ ).toEqual(["c"]);
304
+ });
305
+
297
306
  test("marca sent/skip e respeita includeSent", () => {
298
307
  const sent = stateWith([finding({ id: "a", commentId: 99 })]);
299
308
  const [only] = selectSubmitCandidates(sent);
@@ -0,0 +1,100 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ editorFileLink,
4
+ jetbrainsFileLink,
5
+ vscodeFileLink,
6
+ } from "../server/client/utils/format";
7
+ import { detectEditor, resolveEditor } from "../server/editor";
8
+
9
+ describe("detecção de editor", () => {
10
+ test("VS Code pelo TERM_PROGRAM", () => {
11
+ expect(detectEditor({ TERM_PROGRAM: "vscode" }, "/work/app")).toMatchObject({
12
+ kind: "vscode",
13
+ project: "app",
14
+ });
15
+ });
16
+
17
+ test("VS Code pelas variáveis VSCODE_*", () => {
18
+ expect(detectEditor({ VSCODE_PID: "123" }, "/work/app")).toMatchObject({
19
+ kind: "vscode",
20
+ project: "app",
21
+ });
22
+ });
23
+
24
+ test("JetBrains pelo terminal e produto do bundle", () => {
25
+ expect(
26
+ detectEditor(
27
+ {
28
+ TERMINAL_EMULATOR: "JetBrains-JediTerm",
29
+ __CFBundleIdentifier: "com.jetbrains.pycharm",
30
+ },
31
+ "/work/app",
32
+ ),
33
+ ).toEqual({ kind: "jetbrains", project: "app", product: "pycharm" });
34
+ });
35
+
36
+ test("JetBrains por *_INITIAL_DIRECTORY", () => {
37
+ expect(
38
+ detectEditor({ WEBSTORM_INITIAL_DIRECTORY: "/work/app" }, "/work/app"),
39
+ ).toEqual({ kind: "jetbrains", project: "app", product: "webstorm" });
40
+ });
41
+
42
+ test("produto JetBrains ambíguo cai em idea", () => {
43
+ expect(
44
+ detectEditor({ TERMINAL_EMULATOR: "JetBrains-JediTerm" }, "/work/app"),
45
+ ).toEqual({ kind: "jetbrains", project: "app", product: "idea" });
46
+ });
47
+
48
+ test("sem pistas → none", () => {
49
+ expect(detectEditor({}, "/work/app")).toEqual({
50
+ kind: "none",
51
+ project: "app",
52
+ });
53
+ });
54
+
55
+ test("resolveEditor força vscode/jetbrains/none", () => {
56
+ expect(resolveEditor("vscode", {}, "/work/app")).toMatchObject({
57
+ kind: "vscode",
58
+ });
59
+ expect(resolveEditor("jetbrains", {}, "/work/app")).toMatchObject({
60
+ kind: "jetbrains",
61
+ product: "idea",
62
+ });
63
+ expect(
64
+ resolveEditor("none", { TERM_PROGRAM: "vscode" }, "/work/app"),
65
+ ).toMatchObject({ kind: "none" });
66
+ expect(resolveEditor("desconhecido", {}, "/work/app")).toMatchObject({
67
+ kind: "none",
68
+ });
69
+ });
70
+ });
71
+
72
+ describe("links de editor", () => {
73
+ test("VS Code normaliza o drive e mantém a linha", () => {
74
+ expect(vscodeFileLink("C:\\repo\\src\\a.ts", 12)).toBe(
75
+ "vscode://file/c:/repo/src/a.ts:12",
76
+ );
77
+ });
78
+
79
+ test("JetBrains monta navigate/reference", () => {
80
+ const link = jetbrainsFileLink("C:\\repo\\src\\a.ts", 12, "app", "webstorm");
81
+ expect(link).toContain(
82
+ "jetbrains://webstorm/navigate/reference?project=app&path=",
83
+ );
84
+ expect(link ? decodeURIComponent(link) : "").toContain("C:/repo/src/a.ts:12");
85
+ });
86
+
87
+ test("editorFileLink respeita o tipo detectado", () => {
88
+ expect(editorFileLink("/repo/a.ts", 3, { kind: "vscode", project: "app" })).toBe(
89
+ "vscode://file//repo/a.ts:3",
90
+ );
91
+ expect(
92
+ editorFileLink("/repo/a.ts", 3, {
93
+ kind: "jetbrains",
94
+ project: "app",
95
+ product: "goland",
96
+ }),
97
+ ).toContain("jetbrains://goland/");
98
+ expect(editorFileLink("/repo/a.ts", 3, { kind: "none" })).toBeNull();
99
+ });
100
+ });