/acli y /vercel-cli son skills de tipo utility: no manejan un flujo, le enseñan a la IA a usar bien un binario que el flujo necesita. Este deck junta esas dos, los scripts bun run jira:* que viven alrededor de acli, y el loader de .env y los comandos de arranque y credenciales (setup:doctor, harness:env) que deciden si un servidor MCP arranca con su clave o sin ella.
acli, vercel, gh, bun…
La que tiene la sintaxis, los flags, la auth y las trampas de ese binario.
Valores resueltos de los dueños: .agents/project.yaml, los catálogos, .env.
Releer el issue, el deploy o el archivo. El recibo verde no cuenta.
Ninguna de las dos decide qué hacer: eso es de la skill de flujo que las llama. Ellas responden cómo se hace sin romper nada.
.agents/instructions/agent-tool-resolution.md §6.5 lo llama compuerta dura, no sugerencia: antes de cualquier llamada a Bash que nombre un binario de esa tabla, la IA revisa que su skill esté cargada en la sesión y, si no, la carga. Saltear el paso es volar a ciegas con la sintaxis, los flags, la auth y la semántica de errores.
| Si el comando nombra | Se carga | Qué aporta la skill |
|---|---|---|
| acli · jq | /acli | Sintaxis de slugs, IDs de campos personalizados, y el parseo de la salida --json |
| vercel | /vercel-cli (+ /deploy-to-vercel, /sprint-development) | Verificación, env, debug y rollback; el método de deploy; las etapas de deploy |
| cualquier otro de la tabla | la skill que nombra .agents/instructions/agent-tool-resolution.md §6.5 | La tabla es la única lista; este deck no la copia |
.agents/instructions/agent-tool-resolution.md §6 resuelve las etiquetas de herramienta con el CLI como opción primaria porque gasta menos tokens. [ISSUE_TRACKER_TOOL] es /acli; el MCP de Atlassian queda como respaldo opt-in.
Escriben la etiqueta ([ISSUE_TRACKER_TOOL]) y la IA la resuelve. Esa indirección es lo que deja cambiar de herramienta sin reescribir la metodología.
Es la fuente autoritativa y va atada al binario instalado. Ojo con acli: un subcomando que no existe cae en la ayuda del padre con exit 0. Hay que leer el cuerpo de la ayuda, no el código de salida.
acli es el CLI oficial de Atlassian. Lo propio de este repo vive en agentic-dev-core/references/acli-integration.md.
Auth fresca. acli jira auth status antes de cualquier mutación en lote y antes de cada bun run jira:sync-*.
Una sesión vencida corta el lote a la mitad con 401 y sin rollback limpio; en un sync, envenena los catálogos en silencio.
Jira de producción desde tu máquina. Transiciones, borrados y ediciones en lote no se deshacen.
--yes está bien en CI contra un alcance acordado; --yes a mano contra producción, no: cada operación pide tu confirmación.
Sale del yaml. --site "$(bun run --silent jira:url --slug)", con email y token desde .env.
La auth es por producto: jira auth no autentica confluence ni admin.
| Operación | Herramienta | Por qué |
|---|---|---|
| Lectura de detalle (ACs, alcance, plan, campos de bug, comentarios) | bun run jira:sync-issues get <KEY> --include-comments | acli view devuelve null en los campos personalizados; el sync resuelve slugs y escribe un .md por campo |
| Escritura (crear, transicionar, comentar, vincular, asignar) | /acli | El sync es de solo lectura; toda mutación va por acli |
| Búsqueda trivial (clave, resumen, estado) | acli ... search --paginate --json | Sin --paginate devuelve la primera página y no avisa |
Un campo personalizado es {{jira.<slug>}}, resuelto contra .agents/jira-fields.json; un estado o una transición, contra .agents/jira-workflows.json. Los IDs cambian por workspace, los slugs viajan. Si un slug no resuelve: parar, reportar y correr el sync que corresponde. Nunca un literal de reemplazo.
link create --out X --in Y deja "Y depends on X", al revés de lo que sugieren los nombres. --out lleva el prerrequisito, --in el dependiente. Después de cada vínculo, link list --key <dependiente> --json tiene que mostrar el prerrequisito como outwardIssueKey; si no, se borra y se recrea.
unexpected error, trace id: … es la única señal de depuración que da el backend, y la única que acepta el soporte de Atlassian. Se captura siempre y se reporta.
Jira guarda descripciones, comentarios y campos de texto rico como ADF, un árbol JSON. acli nunca convierte Markdown: un # Título pasado tal cual queda guardado como ese texto literal.
# 1. escribir en Markdown · 2. convertir (valida por defecto) bun .agents/skills/acli/scripts/md-to-adf.ts body.md body.adf.json # 3. medir el valor COMPLETO tal como se va a guardar jq -c . body.adf.json | wc -m # tiene que quedar en 30000 o menos # ADF armado fuera del conversor (payload de create, cuerpo de un PUT): validarlo igual bun .agents/skills/acli/scripts/md-to-adf.ts --check field.adf.json
Nunca ADF a mano. El conversor es determinista, diffeable, respeta el snake_case y frena el bug de marcas combinadas (código inline junto a negrita o cursiva da HTTP 400).
Si se pasa del presupuesto. Jira corta en 32.767 caracteres del ADF serializado, no del Markdown. Pasado el límite la IA para y te propone qué secciones mover y adónde.
Nunca trunca ni parte un valor en dos campos.
Según la superficie. --from-json en workitem create; --description-file en edit; comment create -F; comment update --body-adf.
Los nombres exactos salen de package.json, que es la única fuente. Estos scripts hablan con la API REST de Jira usando las mismas credenciales de .env.
| Script | Hace | Ojo |
|---|---|---|
| jira:url | Imprime el host activo desde .agents/project.yaml → issue_tracker.atlassian_url. Con --slug, sin esquema, para acli --site | Úsalo con --silent dentro de $(...). Nunca recortes el https:// a mano |
| jira:sync-fields | Escribe .agents/jira-fields.json: slug → ID de campo personalizado y sus opciones | Si el catálogo ya está poblado, pide --force |
| jira:sync-workflows | Escribe .agents/jira-workflows.json: estados y transiciones por tipo de trabajo | Lee la clave del proyecto de .agents/project.yaml |
| jira:sync-link-types | Escribe .agents/jira-link-types.json: tipos de vínculo entre issues | Solo a mano: ni setup ni CI lo corren |
| jira:check | Compara .agents/jira-required.yaml contra el catálogo: falta un campo requerido = exit 1 | --live además compara el caché de workflows contra Jira vivo (solo avisos) |
Un push aceptado o un build en cola no dicen que el deploy terminó (Critical Rule #16). La pregunta tiene una sola forma correcta: encontrar el deploy de este commit y bloquear hasta un estado final.
# el SHA del commit que esperas ver desplegado SHA=$(git rev-parse HEAD) # 1. encontrar SU deploy: filtro de metadata + JSON, nunca grep (repetir unos segundos hasta que aparezca) vercel ls -m githubCommitSha="$SHA" --format json | jq -r '.deployments[0].url // empty' # 2. bloquear hasta READY / ERROR / CANCELED · exit 0 solo en READY vercel inspect "https://$URL" --wait --timeout=10m
Es la asimetría que más confunde: vercel deploy bloquea por defecto y vercel inspect no. Al revés, o esperas diez minutos una URL que necesitabas ya, o corres el smoke test contra un build sin terminar.
--status READY funciona; --status ready devuelve vacío y no da error. Y siempre --format json: la tabla humana trae colores ANSI que rompen cualquier regex.
Etapa 4: el commit de la rama de integración, git_strategy.branches.integration en .agents/project.yaml. Si vale null, como en solo-main, la superficie de staging es el Preview de la rama del PR. Etapa 5: el deploy de producción.
Las operaciones de alrededor del deploy. El único sondeo seguro en un directorio sin vincular es vercel whoami; ls o link preguntan, o con --yes vinculan como efecto secundario.
| Scope (CLI) | Se usa en | Regla |
|---|---|---|
| production | La rama de producción | Toda clave de runtime que declara .env.example tiene que existir en los dos antes del deploy |
| preview | Cualquier otra rama | |
| development | vercel dev y vercel env pull | El pull escribe .env.local, gitignorado: nunca se commitea |
Diff antes de empujar. Claves locales contra vercel env ls preview --format json; un push a ciegas pisa valores que eran distintos a propósito.
Nunca ATLASSIAN_*, N8N_* ni SUPABASE_ACCESS_TOKEN en Vercel: son credenciales del agente, no del runtime.
Rollback o forward-fix. Si el arreglo es claro, commit y push; si el bug solo se ve en producción, rollback y depurar con calma.
El rollback no deshace migraciones de base, mails enviados ni flags cambiados.
Build: vercel inspect <url> --logs.
Runtime: vercel logs <url> --follow.
Un cambio de env no redeploya solo: hace falta un deploy nuevo.
# rollback: el deploy de producción anterior, promovido; después verificar el alias vercel ls --prod --status READY --format json vercel rollback <url-del-deploy-sano> vercel inspect <alias-de-producción> --wait --timeout=2m
El harness lee su config y lanza los servidores MCP antes de que corra cualquier hook. Cada servidor que necesita una clave arranca con el loader de .env (varlock run --filter), que la lee del .env en ese momento, se haya lanzado el harness como sea. Si la clave falta, el servidor arranca igual y falla recién en su primera llamada autenticada (Critical Rule #9).
| Comando | Qué resuelve | Cuándo |
|---|---|---|
| claude · opencode · codex | El binario pelado, sin script envoltorio: nada carga .env en el proceso del agente, así que ningún secreto llega al shell de la IA. Cada servidor MCP lee .env con el loader de .env | Cada vez que abres el agente desde la terminal (o su app de escritorio) |
| bun run vars:env:check | Nombra la variable que el shell exporta con un valor distinto del de .env (bajo varlock gana el heredado). Solo nombres y largos | Cuando un MCP se comporta como si .env no existiera |
| bun run harness:env | Retira las copias en texto plano que escribía una instalación vieja (el bloque env de .claude/settings.local.json, .auth/opencode/<VAR>): si coincide con .env la borra, si no la mueve a .auth/harness-env-backup/ y la nombra. Nunca imprime un valor | Una vez, en una máquina que corrió la versión vieja. Después de un cambio en .env alcanza con reiniciar la sesión |
| bun run harness:env:check | Sale con 1 mientras quede una de esas copias | Para confirmar que no queda ninguna |
| bun run setup:doctor | Chequeo de solo lectura: env vars, el host de Atlassian, dependencias, el contrato entre harnesses, las copias en texto plano que quedan, de dónde salen los secretos (.env o el gestor de secretos), los MCP de nivel harness que declara esta máquina. Con --json, una lista de acciones pendientes | Al configurar, y ante cualquier fallo raro de herramienta |
Cada una tiene una skill dueña que ya explica su flujo. Aquí va un puntero, no una copia.
| Herramienta | Dueña | Dónde leerla |
|---|---|---|
| bun run context:map | las skills de mapas de negocio (business-*-context) | context-skills · leer el mapa; cómo se genera: project-context |
| bun run git:policy | /git-flow-master | git-flow-master · paridad |
| bun run worktree:provision · worktree:audit | /git-flow-master (y la flota de /orca-orchestration) | git-flow-master · worktrees |
| orca | /orca-orchestration | orca-orchestration · la compuerta |
| playwright-cli | /playwright-cli, con las reglas propias del repo en /sprint-development | sprint-development · app viva |
Son skills sin modos. Casi nunca hace falta nombrarlas: se cargan solas cuando el comando nombra su binario. Cuando quieres forzarlas, así:
La barra de la skill, a través del alias generado .claude/skills.
Sin barra: nombras la skill en la frase. Lee .agents/skills/ nativo.
Mismo patrón que OpenCode, en CLI o Desktop.
# frases que las disparan sin nombrarlas "transiciona estas historias a In Review" # → /acli "ya está vivo el deploy de este commit?" # → /vercel-cli "sincroniza las env vars con Vercel Preview" # → /vercel-cli
Ponen los verbos; el flujo lo maneja otra skill. Las dos resuelven sus categorías complementarias desde .agents/skills/REGISTRY.md.
Las transiciones de Jira (etapas 1, 3 y 4) y la verificación del deploy en las etapas 4 y 5. Deck: staging y producción.
Creación en lote desde CSV y el reporte de sprint por JQL. Deck: product-management.
Dueña del push, la protección de ramas y el PR. Primero se empuja ahí, después se vuelve aquí a verificar. Deck: git-flow-master.