@payconductor/houndsman 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/package.json +1 -1
- package/src/cli/commands/compare-command.ts +112 -0
- package/src/cli/commands/context-command.ts +1 -0
- package/src/cli/commands/doctor-command.ts +22 -0
- package/src/cli/commands/findings-command.ts +11 -1
- package/src/cli/commands/hunt-command.ts +479 -0
- package/src/cli/commands/review-command.ts +58 -2
- package/src/cli/commands/status-command.ts +3 -2
- package/src/cli/help.ts +20 -3
- package/src/cli/index.ts +20 -3
- package/src/cli/progress.ts +50 -0
- package/src/core/ai/ai-client.ts +38 -2
- package/src/core/ai/ai-sdk-model-provider.ts +39 -0
- package/src/core/ai/features/brief-enricher.ts +1 -1
- package/src/core/ai/features/business-analyzer.ts +61 -0
- package/src/core/ai/features/business-summarizer.ts +44 -0
- package/src/core/ai/index.ts +14 -1
- package/src/core/ai/prompts/brief.ts +33 -7
- package/src/core/ai/prompts/business.ts +76 -0
- package/src/core/ai/provider-capabilities.ts +12 -0
- package/src/core/ai/schemas.ts +70 -2
- package/src/core/ai/types.ts +2 -0
- package/src/core/ai/usage-recorder.ts +53 -1
- package/src/core/brief.ts +246 -12
- package/src/core/builtins.ts +108 -0
- package/src/core/business-summary.ts +583 -0
- package/src/core/commands/compare.ts +233 -0
- package/src/core/commands/context.ts +6 -2
- package/src/core/commands/findings.ts +38 -30
- package/src/core/commands/review.ts +6 -0
- package/src/core/commands/scaffold.ts +38 -0
- package/src/core/commands/status.ts +9 -4
- package/src/core/compare.ts +243 -0
- package/src/core/concurrency.ts +30 -0
- package/src/core/config.ts +57 -1
- package/src/core/defaults.ts +63 -2
- package/src/core/domain/budget.ts +62 -4
- package/src/core/domain/classification.ts +12 -2
- package/src/core/engine/carryover.ts +4 -0
- package/src/core/engine/finalize-pipeline.ts +347 -0
- package/src/core/engine/prepare-pipeline.ts +1294 -0
- package/src/core/engine/review-context.ts +51 -0
- package/src/core/engine/review-options.ts +47 -0
- package/src/core/engine/review-scope.ts +106 -26
- package/src/core/engine/review-session.ts +18 -16
- package/src/core/engine/review-workspace.ts +14 -0
- package/src/core/engine/run-artifacts.ts +196 -0
- package/src/core/engine/run-format.ts +4 -0
- package/src/core/env.ts +14 -0
- package/src/core/findings-state.ts +13 -1
- package/src/core/github-review.ts +5 -2
- package/src/core/hunt/hunt-runner.ts +418 -0
- package/src/core/hunt/index.ts +61 -0
- package/src/core/hunt/maki-install.ts +331 -0
- package/src/core/hunt/maki-intent-prompt.ts +50 -0
- package/src/core/hunt/maki-model.ts +101 -0
- package/src/core/hunt/maki-prompt.ts +83 -0
- package/src/core/hunt/maki-release.ts +104 -0
- package/src/core/hunt/maki-runner.ts +276 -0
- package/src/core/hunt/pool.ts +8 -0
- package/src/core/logging/index.ts +7 -0
- package/src/core/logging/logger.ts +19 -0
- package/src/core/logging/progress.ts +366 -0
- package/src/core/orchestrator.ts +33 -1180
- package/src/core/paths.ts +6 -3
- package/src/core/pr-context.ts +2 -4
- package/src/core/prepare-report.ts +61 -0
- package/src/core/project-config.ts +46 -1
- package/src/core/propagation.ts +127 -0
- package/src/core/registry-check.ts +60 -21
- package/src/core/renderers/manifest-writer.ts +13 -0
- package/src/core/renderers/resume-renderer.ts +151 -11
- package/src/core/report-builder.ts +108 -278
- package/src/core/storage/file/file-storage.ts +7 -3
- package/src/core/storage/sqlite/mappers.ts +10 -0
- package/src/core/storage/sqlite/schema.ts +104 -23
- package/src/core/storage/sqlite/sqlite-storage.ts +22 -9
- package/src/core/storage/types.ts +17 -4
- package/src/core/storage/work-area.ts +59 -1
- package/src/core/types.ts +31 -0
- package/src/core/watchdog-registry.ts +21 -3
- package/src/index.ts +78 -4
- package/src/server/client/ArtifactsPanel.tsx +4 -2
- package/src/server/client/FindingCard.tsx +21 -1
- package/src/server/client/RunsSection.tsx +5 -0
- package/src/server/client/utils/constants.ts +21 -11
- package/src/server/client/utils/markdown.ts +1 -1
- package/src/server/client/utils/types.ts +14 -2
- package/src/server/run-repository.ts +35 -14
- package/src/server/styles.css +2 -0
- package/src/skill/OUTPUT-FORMAT.md +36 -10
- package/src/skill/README.md +30 -14
- package/src/skill/SETUP.md +22 -1
- package/src/skill/SKILL.md +17 -4
- package/src/skill/example-resume.md +43 -0
- package/src/skill/hunt.md +181 -58
- package/src/tests/__snapshots__/baseline.test.ts.snap +206 -178
- package/src/tests/ai-core.test.ts +84 -1
- package/src/tests/ai-features.test.ts +95 -4
- package/src/tests/baseline.test.ts +29 -121
- package/src/tests/brief-scope.test.ts +250 -0
- package/src/tests/business-summary.test.ts +258 -0
- package/src/tests/cli.test.ts +1 -0
- package/src/tests/compare.test.ts +233 -0
- package/src/tests/fixtures/fake-model-provider.ts +2 -1
- package/src/tests/hunt-cli.test.ts +185 -0
- package/src/tests/hunt-install.test.ts +153 -0
- package/src/tests/hunt-maki.test.ts +525 -0
- package/src/tests/markdown.test.ts +3 -3
- package/src/tests/progress.test.ts +145 -0
- package/src/tests/propagation.test.ts +119 -0
- package/src/tests/review-resume.test.ts +60 -1
- package/src/tests/review-scope.test.ts +90 -19
- package/src/tests/review-scripts.test.ts +106 -218
- package/src/tests/server.test.ts +7 -4
- package/src/tests/storage.test.ts +194 -9
- package/src/core/renderers/report-renderer.ts +0 -66
- package/src/skill/example-review.md +0 -66
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ agente especialista por domínio que audita o diff · **review/revisão** = o pr
|
|
|
15
15
|
**Jev** = pré-análise opcional. O vocabulário completo (brief, findings, manifest, budget,
|
|
16
16
|
overflow, churn, candidato…) está em [`docs/GLOSSARY.md`](docs/GLOSSARY.md).
|
|
17
17
|
|
|
18
|
+
> Toda revisão tem duas etapas fixas: um **modelo de intenção** da PR
|
|
19
|
+
> (`business-summary.json`) e o revisor de fluxo **`general`**. Nenhum dos dois é
|
|
20
|
+
> configurável — o primeiro enriquece os briefs; o segundo procura gaps de lógica/domínio,
|
|
21
|
+
> não só violações de regra.
|
|
22
|
+
|
|
18
23
|
---
|
|
19
24
|
|
|
20
25
|
## Como iniciar
|
|
@@ -28,9 +33,11 @@ diferente das PRs. O motor só coordena a caçada — as regras são problema se
|
|
|
28
33
|
- **git** — o diff usa `git diff <base>...<head>`.
|
|
29
34
|
- **gh** (Opcional) — Obtém contexto da PR e dispara comentários para o GitHub
|
|
30
35
|
- **`HOUNDSMAN_TYPE_SAFE_API_KEY`** (opcional) — pré-análise Jev.
|
|
31
|
-
- **`HOUNDSMAN_DEEP_SEEK_API_KEY`** (opcional) — features de IA (briefs, contexto, classificação, resumo técnico)
|
|
36
|
+
- **`HOUNDSMAN_DEEP_SEEK_API_KEY`** (opcional) — features de IA (briefs, contexto, classificação, resumo técnico) e harness local `hunt`.
|
|
32
37
|
- **`HOUNDSMAN_OPENAI_API_KEY`** / **`HOUNDSMAN_ANTHROPIC_API_KEY`** (opcional) — apenas se
|
|
33
38
|
`ai.provider` for `openai`/`anthropic`.
|
|
39
|
+
- **maki** (opcional) — só para o harness local `houndsman hunt`; a skill `/houndsman hunt`
|
|
40
|
+
não precisa dele. Instale com `houndsman hunt install`.
|
|
34
41
|
|
|
35
42
|
> Coloque as chaves em um `.env` na raiz do projeto; o Bun carrega automaticamente.
|
|
36
43
|
|
|
@@ -84,6 +91,20 @@ O skill orquestra tudo e persiste o relatório no storage global (SQLite em
|
|
|
84
91
|
`<HOUNDSMAN_HOME>/houndsman.db`); a UI lê os artefatos direto do storage. Não precisa rodar
|
|
85
92
|
nenhum script à mão.
|
|
86
93
|
|
|
94
|
+
#### Alternativa: rodar 100% pelo CLI (opcional)
|
|
95
|
+
|
|
96
|
+
Quem não quer usar um agente externo pode executar a mesma revisão pelo terminal, com o
|
|
97
|
+
maki como harness local. A instalação é única e fica fora do projeto:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
houndsman hunt install # baixa o maki fixado em <HOUNDSMAN_HOME>/tools/maki
|
|
101
|
+
houndsman hunt doctor # diagnóstico (não falha se o maki não existir)
|
|
102
|
+
houndsman hunt --pr 456 # prepare → maki (1 processo por brief) → finalize
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Os dois caminhos produzem os mesmos artefatos e usam o mesmo staging. A skill
|
|
106
|
+
`/houndsman hunt` continua sendo o caminho canônico para agentes externos.
|
|
107
|
+
|
|
87
108
|
### 7. Várias PRs ao mesmo tempo (worktrees)
|
|
88
109
|
|
|
89
110
|
Cada revisão pode rodar em um **worktree dedicado** (detached no commit da PR), sem
|
|
@@ -112,7 +133,7 @@ mostra o worktree ativo de cada PR e permite criá-lo pela sidebar.
|
|
|
112
133
|
├─ Passo 0 contexto da PR (gh, opcional)
|
|
113
134
|
├─ Passo 1 classifica o diff → briefs por watchdog (incremental quando possível)
|
|
114
135
|
├─ Passo 2 N subagentes revisam em paralelo
|
|
115
|
-
├─ Passo 3
|
|
136
|
+
├─ Passo 3 grava o resumo final (review-resume.md) + triagem no storage
|
|
116
137
|
├─ Passo 4 valida na UI (server) e marca os achados a executar
|
|
117
138
|
├─ Passo 5 fix corrige o grupo atual → status fix_done
|
|
118
139
|
└─ Passo 6 verify confere o que resolveu, sem re-revisar
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payconductor/houndsman",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comando `compare` — teste A/B entre duas runs (por slug ou branch+variantes).
|
|
3
|
+
*
|
|
4
|
+
* Exemplo:
|
|
5
|
+
* houndsman compare --branch feature/x --a baseline --b otimizado
|
|
6
|
+
* houndsman compare feature-x--baseline-1a2b3c feature-x--otimizado-9f8e7d
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { listRunsForCompare, runCompare } from "../../core/commands/compare";
|
|
10
|
+
import { ArgParser } from "../arg-parser";
|
|
11
|
+
import type { CliContext, Command } from "./command";
|
|
12
|
+
|
|
13
|
+
/** Flags com valor (não entram nos positionais). */
|
|
14
|
+
const VALUE_FLAGS = new Set(["--branch", "--a", "--b", "--config"]);
|
|
15
|
+
|
|
16
|
+
export const COMPARE_HELP = `Uso: houndsman compare [<slugA> <slugB>] [flags]
|
|
17
|
+
|
|
18
|
+
Compara duas runs da mesma branch e grava o relatório na run B.
|
|
19
|
+
|
|
20
|
+
Flags:
|
|
21
|
+
--branch <nome> Branch alvo (default: a atual do git)
|
|
22
|
+
--a <variante> Rótulo A (default: run principal)
|
|
23
|
+
--b <variante> Rótulo B (obrigatório sem slugs)
|
|
24
|
+
--list Lista as runs/variantes com contagem por severidade
|
|
25
|
+
--json Saída JSON (sem markdown)
|
|
26
|
+
|
|
27
|
+
Exemplos:
|
|
28
|
+
houndsman compare --branch feature/x --a baseline --b otimizado
|
|
29
|
+
houndsman compare feature-x--baseline-1a2b3c feature-x--otimizado-9f8e7d
|
|
30
|
+
houndsman compare --list --branch feature/x`;
|
|
31
|
+
|
|
32
|
+
/** Argumentos posicionais (ignora flags com valor). */
|
|
33
|
+
function positionals(args: readonly string[]): string[] {
|
|
34
|
+
const result: string[] = [];
|
|
35
|
+
for (let index = 0; index < args.length; index++) {
|
|
36
|
+
const arg = args[index];
|
|
37
|
+
if (arg.startsWith("--")) {
|
|
38
|
+
if (VALUE_FLAGS.has(arg)) index++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
result.push(arg);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class CompareCommand implements Command {
|
|
47
|
+
readonly name = "compare";
|
|
48
|
+
|
|
49
|
+
async run(args: string[], context: CliContext): Promise<number> {
|
|
50
|
+
const parser = ArgParser.from(args);
|
|
51
|
+
if (parser.has("--help") || parser.has("-h")) {
|
|
52
|
+
context.out(COMPARE_HELP);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const engine = await context.engine();
|
|
57
|
+
const branch = parser.value("--branch");
|
|
58
|
+
|
|
59
|
+
if (parser.has("--list")) {
|
|
60
|
+
const runs = listRunsForCompare(engine.storage, engine.repo, branch);
|
|
61
|
+
if (runs.length === 0) {
|
|
62
|
+
context.out("Nenhuma run encontrada.");
|
|
63
|
+
engine.close();
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
context.logger.table([
|
|
67
|
+
["slug", "variante", "findings", "blocking", "attention", "atualizada"],
|
|
68
|
+
...runs.map((run) => [
|
|
69
|
+
run.slug,
|
|
70
|
+
run.variant ?? "(principal)",
|
|
71
|
+
String(run.findings),
|
|
72
|
+
String(run.bySeverity.blocking),
|
|
73
|
+
String(run.bySeverity.attention),
|
|
74
|
+
run.updatedAt,
|
|
75
|
+
]),
|
|
76
|
+
]);
|
|
77
|
+
engine.close();
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const [slugA, slugB] = positionals(args);
|
|
82
|
+
if (slugA && !slugB) {
|
|
83
|
+
context.err(
|
|
84
|
+
"Passe dois slugs (`houndsman compare <a> <b>`) ou use --b <variante>.",
|
|
85
|
+
);
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const result = runCompare(engine.storage, engine.repo, {
|
|
90
|
+
slugA,
|
|
91
|
+
slugB,
|
|
92
|
+
branch,
|
|
93
|
+
variantA: parser.value("--a"),
|
|
94
|
+
variantB: parser.value("--b"),
|
|
95
|
+
});
|
|
96
|
+
if (parser.has("--json")) {
|
|
97
|
+
context.out(result.json.trimEnd());
|
|
98
|
+
} else {
|
|
99
|
+
context.out(result.markdown.trimEnd());
|
|
100
|
+
context.logger.success(
|
|
101
|
+
`Comparação salva em ${result.artifacts.markdown} (run ${result.b.slug}).`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
context.err(error instanceof Error ? error.message : String(error));
|
|
107
|
+
return 1;
|
|
108
|
+
} finally {
|
|
109
|
+
engine.close();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/** Comando `doctor` (alias `check`) — valida config, ambiente e credenciais. */
|
|
2
2
|
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
3
4
|
import type { AiClient } from "../../core/ai/ai-client";
|
|
4
5
|
import { ArgParser } from "../../core/arg-parser";
|
|
5
6
|
import type { AiProviderKind } from "../../core/config";
|
|
6
7
|
import { ENV_NAMES, env } from "../../core/env";
|
|
8
|
+
import { displayPath } from "../../core/paths";
|
|
7
9
|
import { checkRegistry } from "../../core/registry-check";
|
|
8
10
|
import { storageLocation } from "../../core/storage/paths";
|
|
9
11
|
import type { CliContext, Command } from "./command";
|
|
@@ -66,6 +68,26 @@ export class DoctorCommand implements Command {
|
|
|
66
68
|
const ast = await engine.ast.check();
|
|
67
69
|
logger.bullet(`${ast.ok ? "✅" : "⚠️ "} AST: ${ast.detail}`);
|
|
68
70
|
|
|
71
|
+
// Harness local é opcional: a ausência do maki nunca falha o doctor.
|
|
72
|
+
const { makiSourceLabel, probeMakiVersion, resolveMakiBinary } = await import(
|
|
73
|
+
"../../core/hunt/maki-install"
|
|
74
|
+
);
|
|
75
|
+
const maki = resolveMakiBinary(engine.config.hunt?.binaryPath);
|
|
76
|
+
if (!maki) {
|
|
77
|
+
logger.bullet(
|
|
78
|
+
"⚠️ maki (hunt local): não instalado (opcional — `houndsman hunt install`).",
|
|
79
|
+
);
|
|
80
|
+
} else {
|
|
81
|
+
const exists = existsSync(maki.path);
|
|
82
|
+
const version = exists
|
|
83
|
+
? (maki.version ?? probeMakiVersion(maki.path))
|
|
84
|
+
: undefined;
|
|
85
|
+
logger.bullet(
|
|
86
|
+
`${exists ? "✅" : "❌"} maki (hunt local): ${displayPath(maki.path)} ` +
|
|
87
|
+
`(${makiSourceLabel(maki.source)}${version ? `, v${version}` : ""}).`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
69
91
|
const provider = engine.ai.projectConfig.aiProvider;
|
|
70
92
|
const key = keyFor(provider);
|
|
71
93
|
logger.bullet(
|
|
@@ -14,21 +14,31 @@ export class FindingsCommand implements Command {
|
|
|
14
14
|
async run(args: string[], context: CliContext): Promise<number> {
|
|
15
15
|
const engine = await context.engine();
|
|
16
16
|
const sub = ArgParser.from(args).command();
|
|
17
|
+
// Raiz/git do engine: o comando nunca lê `process.cwd()` implicitamente.
|
|
18
|
+
const options = { root: engine.root, git: engine.git };
|
|
17
19
|
const result =
|
|
18
20
|
sub === "verify"
|
|
19
|
-
? await runVerifyCommand(
|
|
21
|
+
? await runVerifyCommand(
|
|
22
|
+
engine.config,
|
|
23
|
+
engine.storage,
|
|
24
|
+
engine.repo,
|
|
25
|
+
args,
|
|
26
|
+
options,
|
|
27
|
+
)
|
|
20
28
|
: sub === "submit"
|
|
21
29
|
? await runSubmitCommand(
|
|
22
30
|
engine.config,
|
|
23
31
|
engine.storage,
|
|
24
32
|
engine.repo,
|
|
25
33
|
args,
|
|
34
|
+
options,
|
|
26
35
|
)
|
|
27
36
|
: runFindingsCommand(
|
|
28
37
|
engine.config,
|
|
29
38
|
engine.storage,
|
|
30
39
|
engine.repo,
|
|
31
40
|
args,
|
|
41
|
+
options,
|
|
32
42
|
);
|
|
33
43
|
|
|
34
44
|
if (result.exitCode !== 0) context.err(result.output);
|