Handbooks
Referencia

Referencia de la CLI

Cada subcomando, cada flag, su variable de entorno y su valor por defecto — además de qué escribe cada comando y con qué código de salida termina.

handbook [global options] <command> [command options]

Cada comando escribe su resultado en stdout como JSON y sus logs en stderr, así que las tuberías funcionan exactamente como esperarías:

handbook analyze --source ~/code/api --work work/api | jq .functions

`--help` se genera, no se escribe

Cada flag de abajo se deriva de un único registro de opciones, así que handbook <cmd> --help siempre lista el flag, su variable de entorno, su variable con ámbito por comando y su valor por defecto. Si esta página y --help alguna vez discrepan, --help tiene razón — y un test de deriva hace fallar el build.

Opciones globales

FlagEfecto
-V, --versionImprime la versión
-v, --verboseLogging de depuración
-q, --quietSolo errores — gana sobre -v
--env <name>Selecciona un entorno: carga .env.<name>.local y .env.<name> antes que .env.local y .env, y prefiere handbook.config.<name>.yaml. Igual que HANDBOOK_ENV
--env-file <path>Carga exactamente este archivo, saltándose la cascada de .env. Un archivo ausente es un error ruidoso, no un fallback. Prefiere HANDBOOK_ENV_FILE — mira el aviso abajo
--config <path>Usa este archivo de configuración en lugar de descubrir el handbook.config.yaml más cercano

Las opciones globales van antes del subcomando:

handbook --env prod -v generate --source ~/code/api --work work/api

`--env-file` choca con un flag de Node

Node >= 20.6 tiene su propio --env-file, y preescanea toda la línea de comandos buscándolo — incluida la parte posterior a la ruta del script, donde en realidad no aplica el archivo. Una ruta que existe llega a Handbooks intacta, pero una ruta que no existe mata el proceso antes:

$ handbook --env-file /gone.env config
node: /gone.env: not found        # node, exit 9, before Handbooks ever runs

Así que el único caso que el flag promete reportar de forma ruidosa es justo el que no puede reportar. HANDBOOK_ENV_FILE hace exactamente lo mismo y no puede ser interceptada:

$ HANDBOOK_ENV_FILE=/gone.env handbook config
handbook: error: ENOENT: no such file or directory, open '/gone.env'

El flag sigue funcionando siempre que el archivo esté realmente ahí, y gana a la variable de entorno cuando ambos están definidos.


analyze

Solo Phase 1: construye el grafo de llamadas estático. Sin LLM, sin clave, gratis.

handbook analyze --source <dir> --work <dir> [--lang <lang>]
FlagPor defectoEnv
--source <dir>obligatorioHANDBOOK_SOURCE / HANDBOOK_ANALYZE_SOURCE
--work <dir>obligatorioHANDBOOK_WORK / HANDBOOK_ANALYZE_WORK
--lang <lang>autoHANDBOOK_LANG / HANDBOOK_ANALYZE_LANG

--lang acepta auto o cualquiera de: cpp csharp dart go java kotlin objc ocaml php python ruby rust scala shell solidity swift typescript zig. auto detecta y fusiona todos los lenguajes en una sola pasada y es casi siempre lo que quieres.

Escribe phase1/graph.json, functions.csv, graph.dot, dropped-calls.json, scan-coverage.json.

files cuenta lo que realmente se leyó y parseó; filesUnparsed cuenta lo que no, y cada uno de esos archivos aparece nombrado con su motivo en scan-coverage.json. Consulta Formatos de los artefactos.

stdout
{
  "language": "multi",
  "files": 412,
  "functions": 3187,
  "edgesKept": 9042,
  "edgesDropped": 611,
  "filesUnparsed": 3
}

generate

El pipeline completo. Necesita un endpoint LLM para cualquier cosa más allá de la Phase 1.

handbook generate --source <dir> --work <dir> [options]

Opciones del pipeline

FlagPor defectoQué hace
--phase <spec>allall · 1 · 2 (=2a+2b+2c) · 2a · 2b · 2c · 3, o una lista con comas
--strategy <s>(la registrada en el directorio de trabajo, si no file)file o member
--skeleton <path>Tu propio skeleton.yaml. Obligatorio con --strategy member
--detail <d>briefProfundidad de las fichas: brief o deep
--synth-mode <m>oneshotoneshot, o doctor para el bucle de reparación actor–crítico
--narrate-lang <l>enen o zh
--max-doctor-rounds <n>6Rondas de convergencia del doctor
--resumefalseOmite los archivos que ya tienen una ficha completa
--refreshfalseIgnora las cachés de Phase 3
--llm-cachefalseCachea las respuestas crudas del LLM en <work>/phase3/cache

Opciones de rendimiento

FlagPor defectoQué hace
--read-workers <n>12Lotes de fichas concurrentes
--read-batch-size <n>(1 para deep, 8 para brief)Archivos por lote de fichas
--max-chars-per-file <n>0Trunca cada archivo a n caracteres; 0 = sin límite
--assign-batch-size <n>25Fichas por lote de asignación
--assign-workers <n>12Lotes de asignación concurrentes
--organize-workers <n>8Llamadas de organización de etapas concurrentes
--narrate-workers <n>8Llamadas de narración concurrentes

Opciones de LLM (compartidas por generate, plan, resync, studio)

FlagPor defectoAlias de entorno
--provider <name>openaiOPENAI_PROVIDER
--model <id>gpt-4o-miniOPENAI_MODEL
--base-url <url>https://api.openai.com/v1OPENAI_BASE_URL
--max-tokens <n>16000OPENAI_MAX_TOKENS
--timeout <sec>300OPENAI_TIMEOUT
--llm-retries <n>6
--llm-retry-backoff <sec>3
--llm-concurrency <n>16

La clave de API nunca es un flag. Define OPENAI_API_KEY (o HANDBOOK_LLM_API_KEY) en el entorno o en un archivo .env. Se rechaza en un archivo de configuración, porque los archivos de configuración acaban commiteados.

El cuerpo extra de la petición tampoco es un flag, y se rechaza en un archivo de configuración por el mismo motivo. Define OPENAI_EXTRA_BODY (o HANDBOOK_LLM_EXTRA_BODY) en el entorno: fusiona campos del proveedor en el cuerpo de cada petición — {"thinking":{"type":"disabled"}}, por ejemplo — y, al ser de formato libre, no hay manera de distinguir dentro de él un campo de ajuste de uno de autenticación. Los campos de modelo, mensajes y tokens no se pueden sobrescribir por esa vía.

--base-url es un flag, y es bienvenido en un archivo de configuración: que un equipo apunte todos sus checkouts a un mismo gateway compartido es exactamente para lo que sirve ese archivo. Una URL que lleve credenciales incrustadas (https://user:pass@gw.internal/v1) se rechaza ahí, y solo ahí; deja la credencial en el entorno.

--provider elige el formato del protocolo, no el proveedor: openai (el valor por defecto) habla con cualquier endpoint compatible con OpenAI, que son casi todos; anthropic y gemini están para los dos que no lo son.

stdout
{
  "phasesRun": ["1", "2a", "2b", "2c", "3"],
  "nCards": 412,
  "nStages": 9,
  "nUnassignedFiles": 0,
  "nRegisters": 6,
  "usage": { "promptTokens": 1840221, "completionTokens": 214880, "totalTokens": 2055101 }
}

render

Directorio de trabajo → markdown, y opcionalmente más. Sin LLM.

handbook render --work <dir> [--title <t>] [--html] [--html-single] [--agent-site] [--llms-txt]
FlagPor defectoQué hace
--work <dir>obligatorioEl directorio de trabajo a renderizar
--title <title>System HandbookTítulo del handbook en la salida
--out <dir><work>/handbookDónde escribir
--htmlfalseTambién el sitio HTML multipágina, en <out>/html
--html-singlefalseTambién un único <out>/handbook.html autocontenido
--agent-sitefalseTambién el índice para agentes y sus tablas de hechos, en <out>/agent
--llms-txtfalseTambién llms.txt y llms-full.txt
--source-base-url <url>Enlaza cada ficha de archivo a <url>/<relative path>

Sin --source-base-url la salida no contiene ninguna URL externa, lo cual importa si estás publicando un handbook de un código privado.

--out tiene ámbito exclusivo: su variable de entorno es HANDBOOK_RENDER_OUT, no un HANDBOOK_OUT plano, porque --out significa algo distinto en plan y en skill.


skill

Handbooks renderizado → paquete SKILL para agentes. Sin LLM.

handbook skill --handbook <dir> --out <dir> --name <slug> [options]
FlagPor defectoQué hace
--handbook <dir>obligatorioEl directorio del handbook renderizado
--out <dir>obligatorioDónde va el paquete SKILL
--name <slug>obligatorioSlug en minúsculas con guiones; produce <slug>-handbook
--project <name>(--name)Nombre legible del proyecto usado en la prosa
--work <dir>Añade coverage.json de la asignación de Phase 2
--source <dir>Con --work, añade un hash de contenido por archivo
--agent-dir <dir>Incluye el índice para agentes y sus tablas de hechos en references/agent/
--lang <l>enIdioma del cuerpo de SKILL.md. El frontmatter sigue en inglés

Dos negativas que conviene conocer

--out no debe ser el directorio del handbook, ni un ancestro suyo: la build empieza borrando --out, lo que eliminaría justo aquello que se está empaquetando. Y --lang zh te da un cuerpo en chino con frontmatter en inglés — los runtimes de agentes enrutan según el texto de la descripción, así que traducirlo rompería silenciosamente la selección del skill.


validate

Comprueba un paquete SKILL. Sin LLM. Sale con 2 si falla.

handbook validate --skill <dir> [--source <dir>]
FlagPor defectoQué hace
--skill <dir>obligatorioEl directorio del skill a validar
--source <dir>Vuelve a hashear el código vivo para detectar deriva

Comprueba la estructura, el contrato del frontmatter, la consistencia entre el índice ↔ las páginas de etapa, el esquema de coverage.json y la frescura de los hashes. Los errores y las advertencias van a stderr.


plan

Localización de cambios guiada por el handbook. Necesita un endpoint LLM. Solo lectura.

handbook plan --source <dir> --request "<text>" [--handbook <dir>] [--out <file>]
FlagPor defectoQué hace
--source <dir>obligatorioEl código sobre el que planificar (nunca se escribe en él)
--request <text>obligatorioLa petición de cambio en lenguaje natural
--handbook <dir>Handbooks renderizado o skills/<x>/references. Muy recomendable
--out <file>(stdout)Escribe el plan aquí
--max-turns <n>30Presupuesto de turnos del agente

Más las opciones de LLM compartidas.

Sale con un código distinto de cero si el planificador se rindió — inventó resultados de herramientas, se quedó sin turnos o terminó sin nada aprovechable — en lugar de escribir una disculpa que un script acabaría pasándole a apply.


apply

Aplica los bloques EDIT de un plan. Sin LLM. Sale con 2 si algo no se aplicó.

handbook apply --source <dir> --plan <file> [--dry-run] [--backup-root <dir>]
FlagPor defectoQué hace
--source <dir>obligatorioEl árbol a editar
--plan <file>obligatorioEl plan producido por handbook plan
--dry-runfalseSolo verifica — nunca escribe
--backup-root <dir><source>/.handbook-patchesDónde van las copias de seguridad
stdout
{
  "ok": true,
  "dryRun": false,
  "outcomes": [
    { "index": 1, "file": "src/upload.py", "where": "Uploader.send (~88)", "status": "applied", "line": 88 }
  ],
  "changedFiles": ["src/upload.py"],
  "backupDir": "/repo/.handbook-patches/2026-08-08T14-05-11-204Z",
  "problems": []
}

Estados: applied · created · no-match · ambiguous · file-missing · not-a-file · unsafe-path · undecodable · skipped.


rollback

Restaura un árbol de código desde una copia de seguridad de parches. Sin LLM.

handbook rollback --backup <dir> [--source <dir>] [--force]
FlagPor defectoQué hace
--backup <dir>obligatorioDirectorio de copia que contiene manifest.json
--source <dir>Rechaza una copia que pertenece a otro árbol
--forcefalseRestaura incluso los archivos que cambiaron después del parche

Sin --force, un archivo cuyo hash actual no coincide con el hash posterior al parche se rechaza — restaurarlo destruiría silenciosamente todo lo hecho desde entonces.


resync

Adelanta un handbook después de un cambio de código.

handbook resync --case <dir> --work <dir> [options]
FlagPor defectoQué hace
--case <dir>obligatorioDirectorio del caso: edited/ + plan.md opcional + change.diff opcional
--work <dir>obligatorioEl directorio de trabajo a adelantar
--title <title>System HandbookTítulo usado al volver a renderizar
--no-llm(LLM activado)Solo refresco estructural; la prosa se marca como obsoleta
--no-render(render activado)Omite refrescar las salidas ya renderizadas
--corrections <file>corrections.jsonl; sus archivos amplían el conjunto a refrescar
--detail <d>(igual que el handbook existente)brief o deep para las fichas regeneradas
--narrate-lang <l>(igual que el handbook existente)en o zh

Más las opciones de LLM compartidas.

Dejar --detail y --narrate-lang sin definir es el valor por defecto correcto: sin definir significa "igual a lo que este handbook ya es", así que un resync nunca degrada silenciosamente un handbook deep a brief.


studio

La interfaz web local. Se ejecuta hasta Ctrl-C.

handbook studio [--port <n>] [--host <addr>] [--state-dir <dir>]
FlagPor defectoQué hace
--port <n>4860Puerto en el que escuchar
--host <addr>127.0.0.1Dirección de escucha. Los contenedores necesitan 0.0.0.0
--state-dir <dir>$HOME/.handbook-studioRegistro y directorios de trabajo gestionados

Más las opciones de LLM compartidas — Studio las resuelve desde las mismas capas que cualquier otro comando, así que tanto --model como un bloque llm: de un archivo de configuración llegan a sus jobs.

Poner --host 0.0.0.0 no hace que Studio sea accesible remotamente en ningún sentido útil: la protección CSRF comprueba la cabecera Host, así que una petición que nombre una IP de la LAN se rechaza con 403. Consulta Studio.


config

Imprime la configuración resuelta y de dónde vino cada valor. Sin LLM.

handbook config [--command <name>] [--json] [--check]
FlagPor defectoQué hace
--command <name>generateMuestra solo las opciones que aplican a este subcomando
--jsonfalseSalida legible por máquinas
--checkfalseSolo valida; sale con 2 si algo es inválido o falta
handbook config --command generate      # a table, with provenance per row
handbook config --json | jq '.settings[] | select(.source.kind == "env")'
handbook config --check                 # put this one in CI

Muestra configuración rota a propósito

A diferencia del resto de comandos, config no aborta ante un valor inválido. Un --source ausente se muestra como una fila visible — unset (required) en lugar de tumbar la única herramienta que usarías para depurar exactamente ese problema.


Códigos de salida

CódigoSignificado
0Éxito
1Un error — configuración inválida, un artefacto ausente, una ejecución fallida. Mensaje en stderr, con el prefijo handbook: error:
2Falló una comprobación: validate encontró problemas, apply no se aplicó del todo o config --check encontró algo inválido

2 significa "la herramienta funcionó, y la respuesta es no". Los scripts deberían tratarlo de forma distinta a 1.

Los atajos de pnpm

Desde un clon, cada uno de estos compila primero y reenvía los flags tal cual:

pnpm analyze  --source ~/code/proj --work work/proj
pnpm generate --source ~/code/proj --work work/proj
pnpm render   --work work/proj --html --agent-site --llms-txt
pnpm skill    --handbook work/proj/handbook --out skills/proj --name proj
pnpm validate --skill skills/proj --source ~/code/proj
pnpm plan     --source ~/code/proj --request "…" --out plan.md
pnpm apply    --source ~/code/proj --plan plan.md --dry-run
pnpm rollback --backup ~/code/proj/.handbook-patches/<stamp>
pnpm resync   --case cases/mycase --work work/proj
pnpm studio
pnpm config:show --command generate
pnpm handbook --help

En esta página