@payconductor/houndsman 0.2.0 → 0.4.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 (107) hide show
  1. package/README.md +18 -6
  2. package/package.json +1 -1
  3. package/src/cli/arg-parser.ts +8 -0
  4. package/src/cli/commands/command.ts +23 -0
  5. package/src/cli/commands/context-command.ts +40 -0
  6. package/src/cli/commands/doctor-command.ts +60 -0
  7. package/src/cli/commands/findings-command.ts +38 -0
  8. package/src/cli/commands/help-command.ts +13 -0
  9. package/src/cli/commands/registry.ts +34 -0
  10. package/src/cli/commands/review-command.ts +21 -0
  11. package/src/cli/commands/server-command.ts +37 -0
  12. package/src/cli/commands/setup-command.ts +34 -0
  13. package/src/cli/commands/watchdog-command.ts +34 -0
  14. package/src/cli/help.ts +47 -0
  15. package/src/cli/index.ts +60 -268
  16. package/src/core/arg-parser.ts +48 -0
  17. package/src/core/commands/context.ts +29 -13
  18. package/src/core/commands/findings.ts +185 -260
  19. package/src/core/commands/review.ts +21 -39
  20. package/src/core/commands/scaffold.ts +189 -80
  21. package/src/core/commands/setup.ts +16 -11
  22. package/src/core/commands/update.ts +7 -7
  23. package/src/core/commands/watchdog.ts +8 -80
  24. package/src/core/config.ts +24 -25
  25. package/src/core/defaults.ts +20 -0
  26. package/src/core/diff-source.ts +1 -13
  27. package/src/core/doc-discoverer.ts +25 -15
  28. package/src/core/domain/budget.ts +42 -0
  29. package/src/core/domain/classification.ts +94 -0
  30. package/src/core/domain/diff.ts +53 -0
  31. package/src/core/domain/watchdog.ts +96 -0
  32. package/src/core/engine/carryover.ts +67 -0
  33. package/src/core/engine/houndsman-engine.ts +65 -0
  34. package/src/core/engine/review-scope.ts +95 -0
  35. package/src/core/engine/review-session.ts +79 -0
  36. package/src/core/file-classifier.ts +11 -67
  37. package/src/core/findings-state.ts +193 -100
  38. package/src/core/git-revision.ts +14 -40
  39. package/src/core/git-runner.ts +46 -0
  40. package/src/core/github-review.ts +4 -33
  41. package/src/core/jev/jev-candidates.ts +49 -35
  42. package/src/core/orchestrator.ts +247 -421
  43. package/src/core/paths.ts +26 -11
  44. package/src/core/pr-context.ts +39 -51
  45. package/src/core/project-config.ts +56 -0
  46. package/src/core/renderers/batch-presenter.ts +40 -0
  47. package/src/core/renderers/brief-renderer.ts +23 -0
  48. package/src/core/renderers/findings-presenter.ts +74 -0
  49. package/src/core/renderers/manifest-writer.ts +123 -0
  50. package/src/core/renderers/report-renderer.ts +66 -0
  51. package/src/core/renderers/verify-presenter.ts +50 -0
  52. package/src/core/renderers/watchdog-presenter.ts +100 -0
  53. package/src/core/report-builder.ts +12 -7
  54. package/src/core/run-layout.ts +8 -0
  55. package/src/core/run-status.ts +29 -59
  56. package/src/core/services/clock.ts +12 -0
  57. package/src/core/services/file-classifier.ts +34 -0
  58. package/src/core/services/fix-batch-builder.ts +13 -0
  59. package/src/core/services/gh-client.ts +54 -0
  60. package/src/core/services/git-repository.ts +139 -0
  61. package/src/core/services/project-files.ts +91 -0
  62. package/src/core/services/verifier.ts +58 -0
  63. package/src/core/storage/factory.ts +24 -0
  64. package/src/core/storage/file/file-storage.ts +269 -0
  65. package/src/core/storage/paths.ts +64 -0
  66. package/src/core/storage/repo-identity.ts +70 -0
  67. package/src/core/storage/sqlite/mappers.ts +160 -0
  68. package/src/core/storage/sqlite/schema.ts +108 -0
  69. package/src/core/storage/sqlite/sqlite-storage.ts +427 -0
  70. package/src/core/storage/types.ts +143 -0
  71. package/src/core/storage/work-area.ts +191 -0
  72. package/src/core/verify.ts +2 -1
  73. package/src/core/watchdog-registry.ts +58 -16
  74. package/src/index.ts +57 -6
  75. package/src/server/artifacts.ts +8 -61
  76. package/src/server/client/ArtifactsPanel.tsx +137 -23
  77. package/src/server/client/FindingCard.tsx +37 -6
  78. package/src/server/client/MarkdownText.tsx +101 -0
  79. package/src/server/client/SubmitModal.tsx +9 -4
  80. package/src/server/client/code-highlight.ts +422 -0
  81. package/src/server/client/constants.ts +130 -24
  82. package/src/server/client/json-highlight.ts +55 -0
  83. package/src/server/client/markdown.ts +153 -0
  84. package/src/server/client/types.ts +23 -0
  85. package/src/server/index.ts +155 -245
  86. package/src/server/run-repository.ts +299 -0
  87. package/src/skill/OUTPUT-FORMAT.md +6 -4
  88. package/src/skill/README.md +22 -14
  89. package/src/skill/SETUP.md +4 -4
  90. package/src/skill/fix.md +5 -5
  91. package/src/skill/hunt.md +27 -18
  92. package/src/skill/train.md +1 -1
  93. package/src/skill/verify.md +5 -5
  94. package/src/tests/__snapshots__/baseline.test.ts.snap +825 -0
  95. package/src/tests/baseline.test.ts +555 -493
  96. package/src/tests/cli.test.ts +40 -19
  97. package/src/tests/code-highlight.test.ts +123 -0
  98. package/src/tests/domain.test.ts +328 -0
  99. package/src/tests/findings-commands.test.ts +153 -131
  100. package/src/tests/findings-state.test.ts +6 -3
  101. package/src/tests/fixtures/fake-project.ts +0 -1
  102. package/src/tests/infrastructure.test.ts +205 -0
  103. package/src/tests/markdown.test.ts +89 -0
  104. package/src/tests/review-scripts.test.ts +8 -5
  105. package/src/tests/run-status.test.ts +33 -23
  106. package/src/tests/server.test.ts +108 -75
  107. package/src/tests/storage.test.ts +386 -0
package/README.md CHANGED
@@ -74,8 +74,9 @@ No seu harness (Claude Code / agents), invoque o skill:
74
74
  /houndsman hunt
75
75
  ```
76
76
 
77
- O skill orquestra tudo e grava o relatório em `.houndsman/.runs/<branch-slug>/review.md`.
78
- Não precisa rodar nenhum script à mão.
77
+ O skill orquestra tudo e persiste o relatório no storage global (SQLite em
78
+ `<HOUNDSMAN_HOME>/houndsman.db`); a UI lê os artefatos direto do storage. Não precisa rodar
79
+ nenhum script à mão.
79
80
 
80
81
  ---
81
82
 
@@ -86,9 +87,9 @@ Não precisa rodar nenhum script à mão.
86
87
  ├─ Passo 0 contexto da PR (gh, opcional)
87
88
  ├─ Passo 1 classifica o diff → briefs por watchdog (incremental quando possível)
88
89
  ├─ Passo 2 N subagentes revisam em paralelo
89
- ├─ Passo 3 consolida em review.md + state.json
90
+ ├─ Passo 3 consolida em review.md + triagem no storage
90
91
  ├─ 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 5 fix corrige o grupo atual → status fix_done
92
93
  └─ Passo 6 verify confere o que resolveu, sem re-revisar
93
94
  ```
94
95
 
@@ -111,7 +112,7 @@ O skill tem **4 capabilities**, todas em `/houndsman`:
111
112
  ## Triando os findings (opcional)
112
113
 
113
114
  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
+ no storage global. Você decide o que fazer com cada um — pela
115
116
  **UI local** ou pelo **agente** (auto-fix) — sem abrir o JSON à mão:
116
117
 
117
118
  ```bash
@@ -136,12 +137,23 @@ bun run houndsman findings verify # confere o que resolveu (barato, se
136
137
  > o review completo.
137
138
 
138
139
  > 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
+ > pelo fix não se perdem. O estado mora no storage global (`<HOUNDSMAN_HOME>/houndsman.db`).
140
141
 
141
142
  ---
142
143
 
143
144
  ## Mantendo a configuração
144
145
 
146
+ - **Persistência** → por default, SQLite global em `<HOUNDSMAN_HOME>/houndsman.db`
147
+ (`HOUNDSMAN_HOME` sobrescreve o diretório de dados). Para voltar ao backend de arquivos,
148
+ troque só o provider — a interface `StorageProvider` é a mesma:
149
+
150
+ ```ts
151
+ export const houndsmanConfig = {
152
+ // ...
153
+ storage: { provider: "file", path: "./houndsman-runs" },
154
+ };
155
+ ```
156
+
145
157
  - **Alterar regras** → use a capability **`train`** (`/houndsman train`). Ela prova no código
146
158
  que o padrão existe (evidência) antes de editar e roda as validações.
147
159
  - **Atualizou a lib?** Sincronize os arquivos auto-gerados sem tocar nas suas regras:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payconductor/houndsman",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
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",
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Parser de argv do CLI — reexporta a implementação de `core/`.
3
+ *
4
+ * O parser vive em `core/` para ser reutilizado pelos comandos da biblioteca
5
+ * (`parseReviewArgs`) sem inverter a camada `core → cli`.
6
+ */
7
+
8
+ export { ArgParser } from "../core/arg-parser";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Contrato dos comandos do CLI.
3
+ *
4
+ * `core/` continua devolvendo dados; os comandos decidem o que imprimir e o
5
+ * exit code. O engine é carregado sob demanda (comandos sem projeto não pagam).
6
+ */
7
+
8
+ import type { HoundsmanEngine } from "../../core/engine/houndsman-engine";
9
+
10
+ /** Contexto de execução de um comando. */
11
+ export type CliContext = {
12
+ cwd: string;
13
+ /** Carrega a config do projeto e devolve o engine (lazy). */
14
+ engine: () => Promise<HoundsmanEngine>;
15
+ out: (message: string) => void;
16
+ err: (message: string) => void;
17
+ };
18
+
19
+ /** Um subcomando do binário. */
20
+ export interface Command {
21
+ readonly name: string;
22
+ run(args: string[], context: CliContext): Promise<number>;
23
+ }
@@ -0,0 +1,40 @@
1
+ /** Comando `context` — coleta o contexto da PR via `gh` (Passo 0). */
2
+
3
+ import { runContextCommand } from "../../core/commands/context";
4
+ import { displayPath } from "../../core/paths";
5
+ import { ArgParser } from "../arg-parser";
6
+ import type { CliContext, Command } from "./command";
7
+
8
+ export class ContextCommand implements Command {
9
+ readonly name = "context";
10
+
11
+ async run(args: string[], context: CliContext): Promise<number> {
12
+ const engine = await context.engine();
13
+ const parser = ArgParser.from(args);
14
+ const { paths, context: pr } = runContextCommand(
15
+ engine.config,
16
+ engine.storage,
17
+ engine.repo,
18
+ { branch: parser.value("--branch") },
19
+ );
20
+
21
+ if (pr.available) {
22
+ const src = pr.source;
23
+ const label = [src?.number ? `#${src.number}` : "", src?.title ?? ""]
24
+ .filter(Boolean)
25
+ .join(" ");
26
+ context.out(
27
+ `[houndsman] PR ${label} (${src?.head} → ${src?.base}), ${pr.stats?.files ?? 0} arquivo(s).`,
28
+ );
29
+ context.out(`[houndsman] contexto: ${displayPath(paths.context)}`);
30
+ context.out(`[houndsman] brief: ${displayPath(paths.brief)}`);
31
+ context.out(
32
+ `[houndsman] próximo passo: subagente resume em ${displayPath(paths.summary)}`,
33
+ );
34
+ } else {
35
+ context.out(`[houndsman] contexto indisponível (${pr.reason}): ${pr.detail}`);
36
+ context.out(`[houndsman] marcador: ${displayPath(paths.context)}`);
37
+ }
38
+ return 0;
39
+ }
40
+ }
@@ -0,0 +1,60 @@
1
+ /** Comando `doctor` (alias `check`) — valida config, ambiente e credencial Jev. */
2
+
3
+ import { checkRegistry } from "../../core/registry-check";
4
+ import { storageLocation } from "../../core/storage/paths";
5
+ import type { CliContext, Command } from "./command";
6
+
7
+ export class DoctorCommand implements Command {
8
+ readonly name = "doctor";
9
+
10
+ async run(_args: string[], context: CliContext): Promise<number> {
11
+ const engine = await context.engine();
12
+ context.out("→ Validando configuração do projeto...");
13
+ const { problems, warnings, jevQuestions } = checkRegistry(engine.config);
14
+
15
+ if (problems.length > 0) {
16
+ context.err("\n❌ Problemas na configuração de revisão:\n");
17
+ for (const problem of problems) context.err(` - ${problem}`);
18
+ return 1;
19
+ }
20
+ context.out(
21
+ ` ✅ ${engine.config.watchdogIds.length} watchdogs, ${jevQuestions} pergunta(s) Jev.`,
22
+ );
23
+
24
+ if (warnings.length > 0) {
25
+ context.out(`\n⚠️ ${warnings.length} aviso(s) (não bloqueantes):`);
26
+ for (const warning of warnings) context.out(` - ${warning}`);
27
+ }
28
+
29
+ context.out("\n→ Ambiente:");
30
+ context.out(` ${Bun.which("git") ? "✅" : "❌"} git`);
31
+ context.out(` ${Bun.which("gh") ? "✅" : "⚠️ "} gh (opcional — contexto da PR)`);
32
+ context.out("\n→ Persistência:");
33
+ context.out(` provider: ${engine.storage.kind}`);
34
+ context.out(` ${storageLocation(engine.config)}`);
35
+ context.out(` repo: ${engine.repo.key}`);
36
+ await this.reportJevCredential(context);
37
+ return 0;
38
+ }
39
+
40
+ /**
41
+ * Reporta o estado da credencial Jev: presença e validade (chamada mínima).
42
+ * Fail-open — nunca altera o exit code (`doctor` é diagnóstico).
43
+ */
44
+ private async reportJevCredential(context: CliContext): Promise<void> {
45
+ const { JevClient } = await import("../../core/jev");
46
+ const client = new JevClient();
47
+ if (!client.isConfigured()) {
48
+ context.out(" ⚠️ TYPESAFE_API_KEY (opcional — pré-análise Jev)");
49
+ return;
50
+ }
51
+ const check = await client.validateCredential();
52
+ if (check.ok) {
53
+ context.out(` ✅ Jev: credencial válida (modelo ${check.model}).`);
54
+ } else if (check.kind === "invalid") {
55
+ context.out(` ❌ Jev: credencial inválida — ${check.detail}`);
56
+ } else {
57
+ context.out(` ⚠️ Jev: não foi possível validar — ${check.detail}`);
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,38 @@
1
+ /** Comando `findings` — triagem/correção (inclui verify/submit). */
2
+
3
+ import {
4
+ runFindingsCommand,
5
+ runSubmitCommand,
6
+ runVerifyCommand,
7
+ } from "../../core/commands/findings";
8
+ import { ArgParser } from "../arg-parser";
9
+ import type { CliContext, Command } from "./command";
10
+
11
+ export class FindingsCommand implements Command {
12
+ readonly name = "findings";
13
+
14
+ async run(args: string[], context: CliContext): Promise<number> {
15
+ const engine = await context.engine();
16
+ const sub = ArgParser.from(args).command();
17
+ const result =
18
+ sub === "verify"
19
+ ? await runVerifyCommand(engine.config, engine.storage, engine.repo, args)
20
+ : sub === "submit"
21
+ ? await runSubmitCommand(
22
+ engine.config,
23
+ engine.storage,
24
+ engine.repo,
25
+ args,
26
+ )
27
+ : runFindingsCommand(
28
+ engine.config,
29
+ engine.storage,
30
+ engine.repo,
31
+ args,
32
+ );
33
+
34
+ if (result.exitCode !== 0) context.err(result.output);
35
+ else context.out(result.output);
36
+ return result.exitCode;
37
+ }
38
+ }
@@ -0,0 +1,13 @@
1
+ /** Comando `help` — imprime o uso e encerra. */
2
+
3
+ import { HELP } from "../help";
4
+ import type { CliContext, Command } from "./command";
5
+
6
+ export class HelpCommand implements Command {
7
+ readonly name = "help";
8
+
9
+ async run(_args: string[], context: CliContext): Promise<number> {
10
+ context.out(HELP);
11
+ return 0;
12
+ }
13
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Registry de comandos (CLI) — resolve nome → comando e guarda o fallback.
3
+ */
4
+
5
+ import type { Command } from "./command";
6
+
7
+ /** Mapa de comandos por nome, com um comando default. */
8
+ export class CommandRegistry {
9
+ private readonly commands = new Map<string, Command>();
10
+ private fallback: Command | undefined;
11
+
12
+ /** Registra um comando (ou o fallback, quando `fallback`). */
13
+ register(command: Command, options: { fallback?: boolean } = {}): this {
14
+ if (options.fallback) this.fallback = command;
15
+ else this.commands.set(command.name, command);
16
+ return this;
17
+ }
18
+
19
+ /** Registra o mesmo comando sob outro nome (alias). */
20
+ registerAs(name: string, command: Command): this {
21
+ this.commands.set(name, command);
22
+ return this;
23
+ }
24
+
25
+ /** Resolve o comando pelo nome; sem nome → fallback. */
26
+ resolve(name: string | undefined): Command | undefined {
27
+ return name ? this.commands.get(name) : this.fallback;
28
+ }
29
+
30
+ /** Nomes registrados (sem o fallback). */
31
+ names(): string[] {
32
+ return [...this.commands.keys()];
33
+ }
34
+ }
@@ -0,0 +1,21 @@
1
+ /** Comando default — prepara ou finaliza a revisão. */
2
+
3
+ import { parseReviewArgs } from "../../core/commands/review";
4
+ import type { CliContext, Command } from "./command";
5
+
6
+ export class ReviewCommand implements Command {
7
+ readonly name = "";
8
+
9
+ async run(args: string[], context: CliContext): Promise<number> {
10
+ const engine = await context.engine();
11
+ const options = parseReviewArgs(args);
12
+ const result = await engine.session(options).run();
13
+
14
+ for (const message of result.messages) context.out(message);
15
+
16
+ if ("blockers" in result && options.failOnBlocking && result.blockers > 0) {
17
+ return 1;
18
+ }
19
+ return 0;
20
+ }
21
+ }
@@ -0,0 +1,37 @@
1
+ /** Comando `server` — sobe a UI local de triagem. */
2
+
3
+ import { ArgParser } from "../arg-parser";
4
+ import type { CliContext, Command } from "./command";
5
+
6
+ /** Abre a URL no navegador padrão (best-effort, por plataforma). */
7
+ function openBrowser(url: string): void {
8
+ try {
9
+ const command =
10
+ process.platform === "win32"
11
+ ? ["cmd", "/c", "start", "", url]
12
+ : process.platform === "darwin"
13
+ ? ["open", url]
14
+ : ["xdg-open", url];
15
+ Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
16
+ } catch {
17
+ // best-effort — se não abrir, o usuário copia a URL.
18
+ }
19
+ }
20
+
21
+ export class ServerCommand implements Command {
22
+ readonly name = "server";
23
+
24
+ async run(args: string[], context: CliContext): Promise<number> {
25
+ const engine = await context.engine();
26
+ const parser = ArgParser.from(args);
27
+ const port = Number(parser.value("--port")) || 7331;
28
+ const host = parser.value("--host") ?? "127.0.0.1";
29
+
30
+ const { startServer } = await import("../../server");
31
+ const { url } = startServer({ config: engine.config, port, host });
32
+ context.out(`[houndsman] servidor em ${url}`);
33
+ context.out("[houndsman] Ctrl+C para encerrar.");
34
+ if (parser.has("--open")) openBrowser(url);
35
+ return 0;
36
+ }
37
+ }
@@ -0,0 +1,34 @@
1
+ /** Comandos que não dependem da config do projeto: setup, update e test. */
2
+
3
+ import type { CliContext, Command } from "./command";
4
+
5
+ export class SetupCommand implements Command {
6
+ readonly name = "setup";
7
+
8
+ async run(args: string[], context: CliContext): Promise<number> {
9
+ const { runSetupCommand } = await import("../../core/commands/setup");
10
+ const messages = await runSetupCommand({ cwd: context.cwd, args });
11
+ for (const message of messages) context.out(message);
12
+ return 0;
13
+ }
14
+ }
15
+
16
+ export class UpdateCommand implements Command {
17
+ readonly name = "update";
18
+
19
+ async run(args: string[], context: CliContext): Promise<number> {
20
+ const { runUpdateCommand } = await import("../../core/commands/update");
21
+ const messages = await runUpdateCommand({ cwd: context.cwd, args });
22
+ for (const message of messages) context.out(message);
23
+ return 0;
24
+ }
25
+ }
26
+
27
+ export class TestCommand implements Command {
28
+ readonly name = "test";
29
+
30
+ async run(_args: string[], _context: CliContext): Promise<number> {
31
+ const { runLibTests } = await import("../../core/commands/test");
32
+ return runLibTests();
33
+ }
34
+ }
@@ -0,0 +1,34 @@
1
+ /** Comando `watchdog` — contexto de um watchdog. */
2
+
3
+ import { renderWatchdogContext } from "../../core/commands/watchdog";
4
+ import { ArgParser } from "../arg-parser";
5
+ import type { CliContext, Command } from "./command";
6
+
7
+ export class WatchdogCommand implements Command {
8
+ readonly name = "watchdog";
9
+
10
+ async run(args: string[], context: CliContext): Promise<number> {
11
+ const engine = await context.engine();
12
+ const parser = ArgParser.from(args);
13
+ const id = args.find((arg) => !arg.startsWith("-"));
14
+
15
+ if (!id) {
16
+ context.err("Informe o id do watchdog: houndsman watchdog <id>");
17
+ context.err(`Ids válidos: ${engine.config.watchdogIds.join(", ")}`);
18
+ return 1;
19
+ }
20
+
21
+ const output = renderWatchdogContext(engine.config, id, {
22
+ withRules: parser.has("--rules"),
23
+ json: parser.has("--json"),
24
+ });
25
+ if (output === null) {
26
+ context.err(`Watchdog '${id}' não existe.`);
27
+ context.err(`Ids válidos: ${engine.config.watchdogIds.join(", ")}`);
28
+ return 1;
29
+ }
30
+
31
+ context.out(output);
32
+ return 0;
33
+ }
34
+ }
@@ -0,0 +1,47 @@
1
+ /** Texto de ajuda do CLI (mantido fora do bootstrap para reduzir acoplamento). */
2
+ export const HELP = `Uso: houndsman [comando] [flags]
3
+
4
+ Comandos:
5
+ (nenhum) Prepara a revisão (classification + briefs + manifesto)
6
+ --finalize Consolida os findings em review.md
7
+ context Coleta o contexto da PR via gh (Passo 0)
8
+ findings <sub> Triagem/correção dos findings (list · show · set · done · reset · sync · summary · batch · verify · submit · path)
9
+ server Sobe a UI local de triagem (--port, --host, --open)
10
+ setup Cria a configuração inicial do projeto (config + skills)
11
+ update Sincroniza os arquivos auto-gerados (.houndsman/core)
12
+ doctor Valida a configuração, o ambiente e a credencial Jev
13
+ test Roda a suíte de testes da lib
14
+ watchdog <id> Contexto de um watchdog (--rules, --json)
15
+
16
+ Flags:
17
+ --config <path> Caminho do houndsman.config.* (default: detecta no cwd)
18
+ --base <branch> Base do diff (default: main)
19
+ --head <ref> Head do diff (default: HEAD)
20
+ --branch <nome> Branch alvo (default: detecta do git)
21
+ --clean Descarta findings brutos do staging anterior
22
+ --full Força review completo (ignora o modo incremental)
23
+ --no-jev Desativa a pré-análise Jev
24
+ --fail-on-blocking Exit 1 se houver bloqueantes (com --finalize)
25
+
26
+ Flags de findings:
27
+ --decision <d> pending | execute | ignore
28
+ --exec <e> pending | in_progress | done | stale
29
+ --comment <text> Comentário/instrução adicional
30
+ --watchdog <id> Filtra por watchdog (list)
31
+ --severity <s> Filtra por severidade (list, batch)
32
+ --file <text> Filtra por trecho do caminho (list)
33
+ --all Inclui findings ausentes do último relatório
34
+ --json Saída JSON
35
+ --limit <n> Limita o nº de itens (list)
36
+ --group <key> Grupo alvo no batch (default: o primeiro)
37
+ --offset <n> Paginação dentro do grupo (batch)
38
+ --include-done Inclui já concluídos no batch
39
+ --apply (verify) Marca os resolvidos como done
40
+ --fail-on-persisting (verify) Exit 1 se algum blocking persistir
41
+ --include-sent (submit) Reenvia findings já enviados à PR
42
+ --dry-run (submit) Só a prévia do envio, sem postar
43
+
44
+ Persistência:
45
+ Runs, findings e artefatos ficam no storage global (SQLite em
46
+ <HOUNDSMAN_HOME>/houndsman.db). Use storage.provider: "file" na config para
47
+ voltar ao backend de arquivos. Nada é gravado em .houndsman/.runs.`;