Versão: 2.0.0 — camada de apresentação sobre dois contratos congelados. Caminho 1 é o
hpp init, o instalador do harness (hpp/wizard.py, seis estágios, plano e depois--apply). Caminho 2 é o instalador de módulos,installers/kit-forge-1.5.4/kit_doctor.py, cuja mecânica vive emINSTALL-CONTRACT.mde cujo README por módulo segue oINSTALL-GUIDE-TEMPLATE.md; os dois viajam na raiz deste repositório. Este documento descreve a EXPERIÊNCIA: como um AGENTE (Claude Code ou Codex CLI) guia um HUMANO pela instalação, numa conversa. Nada aqui muda a mecânica — se este documento contradisser o código ou oINSTALL-CONTRACT.md, eles vencem. Toda saída dohpp initcitada no Caminho 1 foi medida em 2026-09-23 contra a versão 2.5.8, a partir de um clone deste repositório na tagv2.5.8, salvo onde o texto diz "instalação por pip"; as saídas dokit_doctor.pyno Caminho 2 foram capturadas em 2026-09-21 contra a versão 2.4.1, e o exemplo de ponta a ponta diz o que mudou desde então. O que está marcado como novo na 2.6.0 chegou depois da versão medida aqui.
01
Princípio
O humano nunca vê um prompt de terminal. Ele vê uma conversa. O agente roda o plano, traduz
o plano para a língua do humano, e espera o humano dizer "pode aplicar" em linguagem natural. Só
então re-invoca com --apply. Nenhum input() é alcançado numa sessão de agente — o hpp init
só pergunta num TTY, e --non-interactive, --yes, --json ou CI no ambiente tiram esse
caminho; o "Confirm" é a dupla invocação.
Três papéis, sem sobreposição:
| Papel | Faz | Nunca faz |
|---|---|---|
instalador (hpp init · kit_doctor.py) | executa os seus estágios; em modo plano, zero escrita | pergunta em stdin numa sessão de agente; escreve settings/hooks |
| agente | roda comandos, traduz o plano, pede confirmação, reporta com saída real | aplica sem confirmação; edita settings.local.json/hooks por conta própria |
| humano | lê o plano na conversa, decide, confirma em linguagem natural | precisa decorar flags — o agente carrega o comando |
02
Caminho 1 — hpp init, a jornada do harness (principal)
O hpp init é a porta de entrada: ele planeja a instalação inteira para um host, verifica o que
consegue, escreve no máximo um arquivo e imprime no bloco de wiring as linhas do instalador de
módulos do Caminho 2. O agente começa por aqui, sempre.
Os seis estágios
Os estágios são fixos e rodam nesta ordem; cada linha de abertura completa só quando o estágio terminou.
| Estágio | Linha de abertura | O que mede | O que pode pará-lo |
|---|---|---|---|
detect | detecting host... | greenfield, in-progress ou re-run, a partir de .claude/settings*.json, AGENTS.md, .agents/, .hpp/events.jsonl, um .hpp/profile.json anterior e a contagem de commits do git; lista o que existe e é mantido | um log de eventos ou profile corrompido é um aviso com o arquivo a inspecionar |
prereqs | checking prerequisites... | Python igual ou acima de 3.10; o contrato do manifesto; a distribuição quando o marketplace.json está ao lado do manifesto; git no PATH | Python abaixo do piso ou divergência manifesto/marketplace interrompe (exit 2) |
profile | mounting profile... | quatro respostas — host, bundle, modo de política e um conselheiro de decisão opcional (novo na 2.6.0; padrão off, nunca contado como default pendente) — vindas de flags, de --profile, do prompt (só em TTY) ou do default, com a origem de cada uma registrada | um profile gravado com respostas diferentes é um conflict; nada é sobrescrito |
configure | loading modules... | o plano de módulos para o host, native ou explicit-command por módulo, e o CHECKSUMS.txt de cada diretório de módulo presente | um módulo sem suporte no host, ou um checksum divergente, interrompe (exit 2) |
wire-suggest | wiring suggestions... | o bloco a colar: no Claude Code, linhas de plugin, mais linhas do instalador de módulos para os módulos explicit-command dele; no Codex CLI, codex plugin marketplace add e um codex plugin add por módulo que carrega skills (só skills), depois uma linha do instalador de módulos por módulo; o comando de política como configurado; antes do bloco, uma tabela HOOK CAPABILITIES — cada hook que os módulos escolhidos declaram, com seus eventos, sua política de saída (observe, warn, block) e seus grupos de capacidade — contada na linha de abertura (17 hooks declaring capabilities para reliable-coding) | nada; ele nunca escreve |
smoke | verifying evidence... | quatro controles: classificador de política, grafo de capacidades, log de eventos, benchmark em k=1 | um controle reprovado põe exit 1 e se nomeia |
Plano, depois --apply
- Plano. O agente roda
python -m hpp init --target <projeto> --non-interactive --no-animation(a partir de um clone deste repositório) ouhpp init --target <projeto> --non-interactive --no-animation(a partir de uma instalação por pip). Modo plano é o default: nada é escrito, o alvo tem os mesmos arquivos depois, exit 0. - Tradução. O agente cola na conversa o bloco de confirmação (molde abaixo): o que o
detectviu, as quatro respostas (a quarta, o conselheiro de decisão, é opcional) e de onde cada uma veio, a linha de prontidão com o que foi e o que não foi verificado, a tabela HOOK CAPABILITIES lida em voz alta, o bloco de wiring e as páginas que a seção DOCUMENTATION nomeou. - Confirmação humana. O humano responde em linguagem natural — "pode aplicar", "aplica",
"sim". Qualquer coisa que não seja confirmação clara = não aplica. Se o humano quiser outra
resposta (outro host,
enforceem vez deaudit, uma lista explícita de módulos), o agente roda o plano de novo com--host,--bundle,--policy-modeou--modulese pergunta outra vez. - Apply. O agente re-invoca o MESMO comando com
--apply. Exatamente um arquivo é escrito,.hpp/profile.jsondentro do alvo; a linha de abertura dizwrittene a linha de prontidão ganha um item verificado:
> mounting profile... ✓ written · host=claude-code · bundle=reliable-coding · policy=audit · 3 default(s)
██████████████████░░ 10/11 verified · 1 not verified · 0 failed
✓ profile recorded .hpp/profile.json written
· host wiring not verified — manual gate — paste the block, then run doctor
APPLIED 1 file(s) written inside the target
.hpp/profile.json written host=claude-code · bundle=reliable-coding · policy=audit
- Wiring, pelo humano. O agente mostra o bloco de wiring como impresso e o humano cola:
no Claude Code, linhas
/plugin marketplace adde/plugin install, mais linhas do instalador de módulos para os módulos explicit-command dele; no Codex CLI,codex plugin marketplace adde umcodex plugin addpor módulo que carrega skills (só skills), depois uma linha do instalador de módulos por módulo. Ohpp initnunca editasettings.json, hooks ouAGENTS.md; o wiring do host continuanot verifiedaté o humano tê-lo feito.
Rodar o mesmo comando de novo é seguro: o detect reporta re-run · 1 existing item(s) preserved
e o profile reporta unchanged. Respostas diferentes contra um profile gravado são um
conflict: as chaves divergentes são nomeadas, nada é sobrescrito, exit 1.
> mounting profile... ! conflict · host=claude-code · bundle=reliable-coding · policy=enforce · 2 default(s)
PROBLEMS what was measured, what was expected, what to do
✗ profile [profile]
measured: existing .hpp/profile.json differs in: policy_mode
expected: the same answers as the recorded profile
└ re-run with the recorded answers, or remove .hpp/profile.json to initialise again (nothing was overwritten)
Prontidão por canal
A prontidão conta checagens que rodaram, cada uma com o comando que a reproduz. O que ela
consegue verificar depende de onde o hpp roda, e o agente diz qual canal usou. A partir de um
clone deste repositório — diretórios de módulo, CHECKSUMS.txt e marketplace.json ao lado do
manifesto — um plano contra um alvo vazio mostra:
> detecting host... ✓ greenfield · 0 existing item(s) preserved
> checking prerequisites... ✓ python 3.14.3 · protocol 2.1
> mounting profile... ✓ would-write · host=claude-code · bundle=reliable-coding · policy=audit · 3 default(s)
> loading modules... ✓ 6 modules · reliable-coding · claude-code · 6/6 checksums verified
> wiring suggestions... ✓ 7 commands to paste · 0 files written · 17 hooks declaring capabilities
> verifying evidence... ✓ policy · graph · events · benchmark
> protocol online.
READINESS every line is a check that ran; the command below it reproduces it
████████████████░░░░ 9/11 verified · 2 not verified · 0 failed
Os dois não verificados são o profile (só plano) e o wiring do host (um colar). A partir de uma
instalação por pip, que carrega o harness, o manifesto e a suíte de benchmark, mas nenhum
diretório de módulo e nenhum marketplace.json, o mesmo plano mostra 7/11 verified · 4 not verified:
a integridade da distribuição e os checksums dos módulos são reportados como não verificados
porque não há contra o que medi-los, nunca como aprovados. O agente cita a linha que obteve, não
a linha que esperava.
Depois do NEXT, o relatório imprime uma seção DOCUMENTATION que só nomeia páginas encontradas no
disco. A partir de um clone ela lista INSTALL_FOR_AGENTS.md, docs/CATALOG.md e
docs/MANUAL.html; uma instalação por pip não carrega páginas de documentação, e a seção então
não aparece, em vez de apontar para arquivos que não estão lá. O agente repassa os caminhos que
recebeu e não inventa nenhum.
Flags
| Flag | Efeito |
|---|---|
--target <dir> | o projeto a inicializar (default: diretório atual) |
--apply | escreve .hpp/profile.json; sem ela, só plano |
--host, --bundle, --policy-mode audit|enforce | respondem às três perguntas obrigatórias; --modules a,b substitui o bundle por uma lista explícita |
--decision-advisor off|typesafe|openrouter|compatible | novo na 2.6.0. A quarta pergunta, opcional (padrão off): registra um conselheiro de decisão tipada que você declara e imprime como integrá-lo; o hpp nunca o chama e não guarda chave |
--profile answers.json | as mesmas respostas a partir de um arquivo (chaves host, bundle, policy_mode, modules e, nova na 2.6.0, decision_advisor; chave desconhecida é erro de uso) |
--yes, --non-interactive, --json, ou CI no ambiente | nenhum prompt é alcançado; perguntas sem resposta tomam o default e o relatório diz isso |
--no-animation | saída simples; NO_COLOR é respeitado; sem TTY a saída é completa e sem cor |
--no-benchmark | pula o controle de benchmark no smoke; ele é reportado como não verificado, não descartado em silêncio |
--marketplace <slug> | o slug usado nas linhas /plugin marketplace add (Claude Code) e codex plugin marketplace add (Codex CLI) do bloco de wiring, para forks |
--json | o mesmo relatório em JSON (schema: hpp.init-report/v1), com exit_code, readiness e o detalhe de cada estágio — para CI e para agentes |
Códigos de saída
| Código | Significado | Exemplo medido |
|---|---|---|
0 | plano ou apply concluído; todo controle que rodou passou | os planos citados acima |
1 | um aviso: um controle do smoke reprovou, ou o profile gravado conflita com as respostas dadas | --policy-mode enforce sobre um profile gravado com audit |
2 | uma recusa bloqueante: Python abaixo de 3.10, divergência manifesto/marketplace, módulo sem suporte no host, checksum divergente, id de módulo desconhecido | --host codex --modules claude-dev-kit → loading modules... ✗ bundle custom requires claude-dev-kit, unsupported on codex |
3 | um erro de uso na própria invocação | um arquivo --profile com chave desconhecida → hpp init: profile file has unknown keys: colour (allowed: bundle, host, modules, policy_mode) (a 2.6.0 também lista decision_advisor) |
Com --json, o código que o processo vai devolver também está dentro do relatório (exit_code),
e um estágio que interrompeu a execução deixa os seguintes como not run.
O que o agente diz (molde)
O texto que o agente cola na conversa ao apresentar um plano do hpp init. Placeholders em {};
uma parte em [] só entra quando se aplica — o conselheiro de decisão só quando não é off
(novo na 2.6.0). O molde está escrito em inglês; o agente fala na língua do humano e adapta as
palavras, não a estrutura.
I ran `hpp init` in plan mode against {target} -- nothing has been written. Summary:
**Project diagnosis:** {greenfield | in-progress, preserving: {list} | re-run, profile already recorded}.
**Answers:** host={host} ({flag|profile|default}) · bundle={bundle} ({source}) · policy={policy_mode} ({source})[ · decision-advisor={value} ({source})].
**Readiness:** {n}/11 verified · {m} not verified · {f} failed -- not verified: {items}.
({channel}: {clone of the repository | pip install}; the two extra items a pip install cannot measure are distribution integrity and module checksums.)
**What --apply would do:** write exactly one file, {target}/.hpp/profile.json.
**Hook capabilities:** {n} hooks declaring capabilities -- for each: {id}, {events}, {exit policy}, {capability groups}, as the HOOK CAPABILITIES table prints them[; on Codex CLI none of them is activated].
**Wiring (settings/hooks/plugins):** never automatic. After the apply I bring you the wire block as printed and YOU paste it.
**Documentation:** {the pages the DOCUMENTATION section named | none -- this copy ships no documentation pages}.
If this is fine, say "apply it" and I run the same command with --apply.
03
Caminho 2 — o instalador de módulos (kit_doctor.py)
As linhas do instalador de módulos que o hpp init imprime no bloco de wiring — uma por módulo no
Codex CLI, depois das linhas de plugin, e uma por módulo explicit-command no Claude Code — são este
instalador. Ele copia um módulo por vez para dentro do alvo e roda os smokes declarados do módulo;
planeja primeiro e aplica só numa segunda invocação explícita.
A jornada em 5 passos (igual nos 4 cenários)
- Plano. O agente roda
python installers/kit-forge-1.5.4/kit_doctor.py install --kit <dir-do-módulo> --host <host> --target <projeto> --human(--humanescolhe o relatório legível). Modo plano é o default: nenhuma escrita acontece, exit 0. - Tradução. O agente cola na conversa o bloco de confirmação (molde na seção "Bloco de
confirmação" abaixo): o que o
detectviu, o que o--applyfaria, o que ficou pendente de decisão, e o resultado do smoke. - Confirmação humana. O humano responde em linguagem natural — "pode aplicar", "aplica",
"sim". Qualquer coisa que não seja confirmação clara = não aplica.
Se o humano quiser mudar uma resposta de pergunta (
questions:), o agente escreve um arquivo--answerse roda o plano DE NOVO antes de pedir confirmação outra vez. - Apply. O agente re-invoca o MESMO comando com
--apply. O instalador aplica o profile, roda o smoke de verdade e registra a instalação no registry. - Relato + wiring manual. O agente mostra a saída real do apply. Se o
wire-suggestlistou opções (plugin / bloco de settings), o agente apresenta o conteúdo exato e o humano cola/aciona — mutarsettings.local.json/hooks é gate humano, sempre, mesmo depois do--apply.
O que muda entre os 4 cenários
Tudo abaixo usa saída REAL do kit_doctor.py, que imprime em português. As saídas de
in-progress/re-run/falha foram capturadas contra uma fixture mínima (demo-kit) — mesma
estrutura de report, módulo de exemplo.
1. greenfield — projeto novo
✓ detect
classification=greenfield · new project — no previous config detected
O que o agente enfatiza: não há nada a preservar; o plano é o caminho feliz.
A decisão pedida ao humano é uma só: "o que o --apply faria está ok?".
Exemplo completo end-to-end na última seção deste caminho.
2. in-progress — projeto com config existente
✓ detect
classification=in-progress · project in progress — existing config detected (it will be preserved)
já existe (não será tocado): .claude/settings.local.json: statusLine/hooks já configurados
O que muda na conversa: o agente lista item por item o que já existe e será preservado —
essa é a informação que tira o medo de instalar por cima. O profile nunca sobrescreve
(skip-exists é reportado, não silencioso). Se o humano QUISER substituir algo existente, isso é
uma ação manual dele, fora do instalador.
3. re-run — mesmo par módulo+target já instalado antes
✓ detect
classification=re-run · reinstall — this kit+target pair is already in the registry
já existe (não será tocado): profile.yaml presente (customizado)
...
✓ profile
já existe, preservado: profile.example.yaml -> profile.yaml
O que muda na conversa: o agente diz explicitamente que rodar de novo é seguro e idempotente — nada duplica, customização é preservada. Re-run é o jeito normal de (a) verificar uma instalação antiga e (b) atualizar após puxar uma versão nova do módulo. A pergunta ao humano vira: "quer re-aplicar mesmo assim, ou só queria conferir?".
4. Falha de smoke — o módulo reprovou no próprio self-test
⚠ smoke [FAIL]
self-tests: 1 ok · 0 sem suporte (ignorados)
⚠ FALHOU: scripts\bad_tool.py (exit 1)
RESULTADO: smoke FALHOU — não aplique este kit before de corrigir os self-tests acima.
Depois de corrigir, rode o plano de novo para confirmar before do --apply.
Exit code = 1. O que muda na conversa: o agente NÃO oferece o --apply. Ele reporta qual
script falhou, com exit code, e propõe o próximo passo (investigar o script, verificar
integridade com kit_doctor.py verify <dir-do-módulo>, ou baixar o módulo de novo). Só volta a
oferecer aplicação depois de um plano novo sair limpo. Um humano que insiste em aplicar com smoke
falhando está por conta própria — o agente registra que desaconselhou e por quê.
Perguntas (questions:) na conversa
A maioria dos módulos não tem perguntas (por design). Quando tem, o plano mostra:
✓ configure
sem resposta (default assumido): modo
para responder de verdade: repetir com --answers <arquivo.json|yaml>
Fluxo do agente: (1) apresenta cada pergunta pendente com o default em destaque — "vou usar
full, a menos que você prefira lite"; (2) se o humano escolher algo diferente do default, o
agente escreve um answers.json e roda o plano de novo com --answers answers.json; (3) só
então pede confirmação. Instalar sem responder nada nunca trava — default sempre resolve.
Bloco de confirmação (molde)
O texto que o agente cola na conversa ao apresentar um plano de módulo. Placeholders em {}. As
linhas marcadas [cenário] só entram no cenário correspondente. O molde está escrito em inglês;
o agente fala na língua do humano e adapta as palavras, não a estrutura.
I ran the {kit} installer in plan mode -- nothing has been written yet. Summary:
**Project diagnosis** ({target}):
[greenfield] New project -- no previous config detected.
[in-progress] Project in progress. The installer detected and will PRESERVE:
[in-progress] {existing_config list, one per line}
[re-run] This module was installed in this project before (it is in the registry).
[re-run] Re-applying is safe: nothing is duplicated, your customisation is preserved.
**What --apply would do:**
- {profile actions: "copy profile.example.yaml -> profile.yaml" | "nothing to copy (already exists, preserved)"}
- record the installation in ~/.claude-kits/registry.json
**Module questions:** {"none" | "pending, using default: {id}={default} -- want to change it?"}
**Wiring (settings/hooks):** never automatic. After the apply I bring you the exact block
({paths suggested by wire-suggest}) and YOU decide whether to paste it.
**Smoke (module self-tests):** {N} ok, {N} ignored{" -- ALL passed" | see the failure block below}.
[if smoke ok] If this is fine, say "apply it" and I run:
[if smoke ok] python kit_doctor.py install {kit_dir} --target {target_dir} --apply
[if smoke fail] The smoke FAILED: {file} (exit {N}). I do NOT recommend applying.
[if smoke fail] Next step: {investigate the script | run kit_doctor.py verify {kit_dir}}.
[if smoke fail] Once that is fixed, I run the plan again and bring you the result.
Exemplo end-to-end (greenfield, execução real)
Módulo: operator-kit-1.5.0. Comando que o agente rodou (saída real abaixo). Esta captura é
datada: ela é anterior ao caminho versionado do instalador. Na distribuição atual o módulo é o
operator-kit-1.8.2 e o instalador vive em installers/kit-forge-1.5.2/kit_doctor.py; os
estágios, as flags (--target, --host, --answers, --apply, --human) e a forma da saída
são os mesmos.
$ python installers/kit-forge-1.4.1/kit_doctor.py install frameworks/operator-kit-1.5.0 \
--target frameworks/operator-kit-1.5.0 --human
INSTALL PLAN — operator-kit-1.5.0 (dry run: nothing was written)
target: frameworks\operator-kit-1.5.0
host: claude-code
✓ detect
classification=greenfield · new project — no previous config detected
✓ prereqs
✓ profile
would copy: profile.example.yaml -> operator-profile.yaml
✓ configure
✓ wire-suggest
opção [plugin]: /plugin marketplace add . && /plugin install <name>@<marketplace>
opção [doc]: seguir SETTINGS-WIRE.md
(nenhuma é executada automaticamente — mexer em settings/hooks é decisão humana)
✓ smoke
self-tests: 27 ok · 0 sem suporte (ignorados)
Nada foi modificado. Se o plano está de acordo, aplique com:
python kit_doctor.py install frameworks\operator-kit-1.5.0 --target frameworks\operator-kit-1.5.0 --apply
O que o agente diz ao humano (texto exato, seguindo o molde):
Rodei o instalador do operator-kit-1.5.0 em modo plano — nada foi escrito ainda. Resumo:
Diagnóstico do projeto: projeto novo (greenfield) — nenhuma config prévia detectada, nada a preservar.
O que o --apply faria:
- copiar
profile.example.yaml->profile.yaml(o profile que você vai customizar)- registrar a instalação em
~/.claude-kits/registry.jsonPerguntas do módulo: nenhuma — este módulo não tem nada configurável na instalação.
Wiring (settings/hooks): nunca é automático. Depois do apply, os caminhos são instalar como plugin (
/plugin marketplace add .+/plugin install) ou seguir oSETTINGS-WIRE.md— eu te trago o passo a passo e você decide.Smoke (self-tests do módulo): 27 ok, 0 ignorados — todos passaram.
Se estiver de acordo, me diga "pode aplicar" e eu rodo o mesmo comando com
--apply.
Humano: "pode aplicar"
O agente roda o mesmo comando com --apply, cola a saída real (o cabeçalho vira
INSTALL APPLIED — ... (--apply), a linha do profile vira copied:, o rodapé confirma
Install applied and registered. Wiring of settings/hooks (if suggested above) stays manual.) e fecha com o wiring:
Aplicado e registrado. Falta só o wiring, que é seu: quer que eu te mostre o bloco do
SETTINGS-WIRE.mdpara você colar nosettings.local.json, ou prefere o caminho de plugin? Em qualquer um dos dois, quem executa o passo final é você.
04
O que o agente NUNCA faz (checklist de conduta)
- NUNCA roda
--apply— dohpp initou dokit_doctor.py— sem confirmação explícita do humano NESTA conversa - NUNCA edita
settings.local.json/ hooks /AGENTS.md— apresenta o bloco, o humano cola - NUNCA reporta "instalado" sem colar a saída real do
--apply(exit code incluso) - NUNCA reporta uma linha de prontidão que não obteve — cita o canal e a contagem como impressos
- NUNCA oferece
--applyquando o smoke falhou (exit 1) — corrige primeiro - NUNCA responde as perguntas do módulo sozinho quando o humano expressou preferência —
escreve
--answerse re-planeja - Em re-run, SEMPRE diz que é idempotente antes de pedir confirmação