@northlabssoftware/nl
v0.1.4
Published
North Labs ai-toolkit CLI — spec-driven monorepos for AWS Serverless + Postgres, with the same workflow across Go, Python, NestJS and React.
Maintainers
Readme
@northlabssoftware/nl
CLI oficial del North Labs AI Toolkit.
Un solo binario global para crear proyectos, aplicar el manifest, sumar servicios y mantener el toolkit al día.
Quickstart · Por qué existe · Comandos · Cómo funciona · Migrar desde v1
🚀 Quickstart
# 1. Instalar la CLI una vez en tu máquina
npm install -g @northlabssoftware/nl
# 2. Crear el proyecto + materializar el toolkit
nl init mi-proyecto
cd mi-proyecto
# 3. Abrir Claude Code y dejar que el framework te guíe
claude
> /nl-init # cuestionario 4 fases → constitution + CLAUDE.md
> /nl-feature "..." # tu primera feature, autopilot con gates entre fasesEso es todo el bootstrap. nl init resuelve el último tag del toolkit, hace sparse-clone del boilerplate-root, siembra .northlabs.json, hace git init -b main y corre nl install para materializar .claude/_nl/. Salís listos para /nl-init.
¿Necesitás los pre-requisitos (
bash 4+,jq,docker)? Mirádocs/01-getting-started/installation.md.
🎯 Por qué existe
Antes de @northlabssoftware/nl, arrancar un proyecto eran tres comandos cruzando dos herramientas:
npx degit north-labs-software/ai-toolkit/boilerplate-root mi-proyecto
cd mi-proyecto && git init -b main
./bootstrap.sh
./nl installMás un ./nl per-proyecto que había que mantener sincronizado a mano. Ahora es:
nl init mi-proyectoMismo toolkit. Mismos boilerplates. Mismo layout .claude/_nl/. Solo que ahora nl es un binario global versionado en npm en vez de un script per-proyecto.
| Problema viejo | Cómo lo resuelve @northlabssoftware/nl |
|---|---|
| bootstrap.sh + ./nl per-proyecto que se desfasaban | Un solo binario global, versionado en npm |
| 3 comandos para arrancar | nl init <name> hace los 3 atomicamente |
| Updates del CLI requerían re-correr bootstrap.sh | npm update -g @northlabssoftware/nl |
| Clone full --depth 1 del monorepo entero | Sparse-checkout: solo el subdir que necesitamos (~60-80% menos bandwidth) |
| "¿Qué versión del toolkit estoy usando?" no era trivial | nl --version + cat .northlabs.json \| jq .toolkit_version |
📥 Install
Requiere Node 20+. La CLI en sí tiene cero dependencias npm — shell-out a git, jq, y los scripts bash del toolkit.
# Global (recomendado)
npm install -g @northlabssoftware/nl
# Ad-hoc con npx (sin instalar global)
npx @northlabssoftware/nl init mi-proyectoVerificar:
nl --version # 0.1.x
nl --help⚙️ Comandos
nl <comando> [args...]| Comando | Qué hace |
|---|---|
| nl init <name> | Crea proyecto nuevo: sparse-clone de boilerplate-root + seed manifest + git init + nl install. Reemplaza npx degit + ./bootstrap.sh. |
| nl install | Aplica .northlabs.json — descarga toolkit, clona boilerplates declarados, materializa .claude/_nl/. Idempotente. |
| nl upgrade <version> | Bumpea toolkit_version en el manifest y re-corre install |
| nl add-service <name> <stack> [@tag] | Suma un microservicio nuevo al manifest y clona el boilerplate de stack correspondiente |
| nl status | Diff entre manifest declarado y filesystem real |
| nl catalog | Lista skills, agents, hooks, rules y templates instalados con sus descripciones |
| nl self-upgrade | Refresca el cache del toolkit al último tag (sin tocar el toolkit_version de ningún proyecto) |
nl init <name> [--ref <tag\|branch>] [--no-install]
Crea un proyecto North Labs desde cero.
nl init mi-proyectoQué hace, paso a paso:
- Resuelve el último tag de
ai-toolkit(o respeta--ref/$NL_TOOLKIT_REF) - Sparse-clona solo
boilerplate-root/del monorepo del toolkit ami-proyecto/ - Siembra
.northlabs.jsoncon esa versión del toolkit +project_name=mi-proyecto - Corre
git init -b main - Corre
nl install(saltear con--no-install)
Resultado: un proyecto listo para abrir en Claude Code y correr /nl-init.
Idempotencia con cwd: si corrés
nl init mi-proyectodesde dentro de un directorio que ya se llamami-proyecto, la CLI lo detecta y usa el cwd en vez de crearmi-proyecto/mi-proyecto/anidado.
nl install
nl installLee .northlabs.json y materializa todo lo declarado:
- Clona cada boilerplate (
infra,frontends[],services[]) solo si el directorio destino no existe - Copia
skills/,agents/,hooks/,templates/y lasrules/base + las de los stacks declarados a.claude/_nl/ - Asigna puertos deterministas a los servicios (
8081 + index) - Stampa
last_installen el manifest - Crea
.claude/settings.jsonsolo si no existe
No toca código del proyecto. No sobreescribe boilerplates ya clonados. Re-correr es seguro.
nl upgrade <version>
nl upgrade v2.1.0Actualiza toolkit_version en .northlabs.json y corre nl install (que descarga la versión nueva al cache y rebuildea _nl/).
Equivalente a editar el manifest a mano + correr install, pero con validación de semver y de que el tag exista upstream.
No existe
nl upgrade latesta propósito — uno debería saber a qué versión está moviendo (leer el CHANGELOG primero). Para ver tags recientes:git ls-remote --tags https://github.com/north-labs-software/ai-toolkit | tail -5.
nl add-service <name> <stack> [@tag]
nl add-service ms-orders go
nl add-service ms-emails nestjs
nl add-service ms-reporting python @v2.0.0stack ∈ go, nestjs, python. El @tag es opcional — por default usa el toolkit_version del proyecto.
Qué hace:
- Valida el nombre (kebab-case, prefijo
ms-recomendado) - Valida el stack
- Resuelve el tag (último si no se especifica)
- Appendea al manifest
.boilerplates.services[] - Corre
nl install(que clona el servicio nuevo desde su boilerplate)
Si el nombre ya existe en el manifest → aborta sin tocar nada.
nl status
nl statusReporta diff entre lo declarado en .northlabs.json y lo que existe en disco:
- Toolkit
_nl/presente? ✓ / ✗ - Cache toolkit presente y matcheando el
toolkit_version? ✓ / ✗ - Infra / frontends / cada servicio declarado existe en disco? ✓ / ✗
- Drift detectado: directorios
ms-*que no están en el manifest
Exit code 0 si todo sincronizado, 1 si hay drift (útil en scripts de CI).
nl catalog
nl catalogLista lo que nl install cableó en el proyecto:
- 11 skills instaladas en
.claude/skills/con sus descripciones - 5 agents disponibles
- 6 hooks activos
- Rules base + stack rules cargadas
- Templates disponibles
Útil como sanity-check post-install.
nl self-upgrade
nl self-upgradeRefresca el cache del toolkit al último tag. No cambia el toolkit_version de ningún proyecto — para eso usá nl upgrade <version>.
Para actualizar la CLI en sí (este paquete npm): npm update -g @northlabssoftware/nl.
🔧 Cómo funciona por dentro
La CLI es un wrapper Node delgado sobre los scripts bash del toolkit (ai-toolkit/lib/cmd-*.sh). Esos bash scripts son battle-tested y siguen siendo la source of truth — la CLI solo les da una UX moderna y la distribución vía npm.
@northlabssoftware/nl (Node 20, ESM, 0 deps)
│
├── nl init ─► sparse-clone boilerplate-root + seed manifest + git init + nl install
├── nl install ─► bash $TOOLKIT_DIR/install.sh install
├── nl upgrade ─► bash $TOOLKIT_DIR/install.sh upgrade <version>
├── nl add-service ─► bash $TOOLKIT_DIR/install.sh add-service ...
├── nl status ─► bash $TOOLKIT_DIR/install.sh status
├── nl catalog ─► bash $TOOLKIT_DIR/install.sh catalog
└── nl self-upgrade─► refresca ~/.cache/nl-toolkit/<ref>/ al último tagToolkit cache vive en ~/.cache/nl-toolkit/<ref>/ (override con $XDG_CACHE_HOME). Cada proyecto pinea su toolkit_version — dos proyectos en versiones distintas conviven sin pisarse.
Sparse-checkout se usa en todos los lugares donde solo necesitamos un subdir del monorepo del toolkit (boilerplates individuales, boilerplate-root en nl init). Recorta ~60-80% de bandwidth vs el --depth 1 full-clone que usaba la v1.
🌐 Variables de entorno
| Variable | Para qué sirve | Default |
|---|---|---|
| NL_TOOLKIT_REPO | Override del git remote del toolkit (dev / fork) | https://github.com/north-labs-software/ai-toolkit.git |
| NL_TOOLKIT_REF | Pin a un ref específico (branch o tag) ignorando el manifest | manifest.toolkit_version o último tag |
| NL_TOOLKIT_OWNER | Override de <owner>/<repo> cuando se construyen origins de boilerplates en nl add-service | north-labs-software/ai-toolkit |
| NL_GITHUB_BASE | Override del base URL de GitHub al clonar boilerplates | https://github.com |
| XDG_CACHE_HOME | Dónde se cachea el toolkit | ~/.cache |
Ejemplos
# Desarrollar contra un checkout local del toolkit
NL_TOOLKIT_REPO=file:///path/to/local/ai-toolkit nl install
# Testear contra una branch sin tag
NL_TOOLKIT_REF=feature/new-skill nl install
# Usar un fork (ej: mirror privado del equipo)
NL_TOOLKIT_OWNER=my-org/ai-toolkit-fork nl add-service ms-orders go🔄 Migrar desde ./nl legacy
Proyectos viejos con ./nl per-proyecto + bootstrap.sh siguen andando — ambas CLIs leen el mismo ~/.cache/nl-toolkit/ y el mismo .northlabs.json. Migrá cuando te quede cómodo:
# 1. Instalar la CLI global junto al ./nl per-proyecto existente
npm install -g @northlabssoftware/nl
# 2. Verificar que la global resuelve al mismo toolkit (debería matchear ./nl status)
nl status
# 3. Borrar los archivos per-proyecto cuando estés tranquilo
rm ./nl bootstrap.sh
git add -A && git commit -m "chore: migrate to @northlabssoftware/nl global CLI"No hay cambios de flags, no hay bump de schema del manifest. Mismo comportamiento, distinto invocador.
Guía completa de v1 → v2 (consolidación de skills, agents retirados, etc.):
docs/MIGRATION_v1_to_v2.md.
🩺 Troubleshooting
| Síntoma | Causa probable | Fix |
|---|---|---|
| nl: command not found después de npm install -g | El bin global de npm no está en $PATH | echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc |
| .northlabs.json not found in <dir> | Estás corriendo nl install afuera de un proyecto | cd al directorio del proyecto, o corré nl init <name> primero |
| could not clone ... at ref <tag> | Typo en toolkit_version, sin red, o repo privado sin auth | Revisá el manifest; gh auth login o GITHUB_TOKEN; verificá que el tag exista upstream |
| bash 4+ required | macOS trae bash 3.2 por default | brew install bash y asegurate que /opt/homebrew/bin esté antes que /bin en $PATH |
| sparse-checkout command failed | git < 2.27 | La CLI hace auto-fallback a --depth 1 full-clone; no es fatal |
| Dos proyectos North Labs colisionan en puerto Postgres 5433 | Los puertos de infra/.env no tienen offset | Ya resuelto: nl install siembra offsets per-proyecto vía sha1(project_name) |
| npm WARN deprecated al instalar | Falsa alarma — el paquete tiene 0 dependencias | Ignorar; si persiste, npm cache clean --force |
📦 Layout del paquete
cli/
├── package.json @northlabssoftware/nl, type: module, Node 20+
├── bin/
│ └── nl ESM entrypoint
├── src/
│ ├── index.js argv parser + command dispatcher
│ ├── commands/
│ │ ├── init.js crea proyecto desde boilerplate-root
│ │ ├── install.js delega a cmd-install.sh
│ │ ├── upgrade.js delega a cmd-upgrade.sh
│ │ ├── add-service.js delega a cmd-add-service.sh
│ │ ├── status.js delega a cmd-status.sh
│ │ ├── catalog.js delega a cmd-catalog.sh
│ │ └── self-upgrade.js refresca el cache
│ └── lib/
│ ├── toolkit.js resuelve + cachea el toolkit por versión
│ └── exec.js spawnea bash y pipea stdio
└── README.md este archivoTodo ESM. Cero dependencias npm. ~600 líneas total.
🛣️ Roadmap
v0.1.x es la versión bash-wrapper — sale rápido, comportamiento predecible, y diferimos rewrites hasta validar la superficie en proyectos reales.
| Versión | Foco | Estado |
|---|---|---|
| v0.1.x | Distribución vía npm + sparse-checkout + multi-frontend | ✅ shipped |
| v0.2 | nl doctor para verificar prereqs (git ≥ 2.27, bash 4+, jq, docker) y diagnosticar fallas comunes | 📋 planeado |
| v0.3 | Test suite node --test con fixtures E2E en CI | 📋 planeado |
| v0.4 | Shell completions (zsh, bash) | 📋 planeado |
| v0.5 | nl migrate para limpiar ./nl + bootstrap.sh de proyectos legacy | 📋 planeado |
| v1.0 | Reescribir incrementalmente los comandos bash en Node — drop bash 4+ requirement | 🔮 futuro |
Sin fechas fijas — driven by real-world use.
🤝 Contribuir
La CLI vive en cli/ dentro del monorepo north-labs-software/ai-toolkit. Issues y PRs van allá.
Desarrollo local
git clone https://github.com/north-labs-software/ai-toolkit.git
cd ai-toolkit/cli
npm link # hace que `nl` resuelva a tu checkout local
nl --version # debería matchear cli/package.jsonTestear contra un toolkit local
# Apunta a tu checkout local del monorepo en vez del repo público
NL_TOOLKIT_REPO=file://$(pwd)/.. nl init /tmp/test-projRelease
Los releases se disparan automáticamente al pushear un tag cli-v* a main:
# Bump version
npm --prefix cli version patch # o minor / major
git push origin main --tagsEl workflow .github/workflows/cli-release.yml se encarga del resto:
- OIDC Trusted Publishing → npm (sin NPM_TOKEN de larga duración)
- Validación de que el tag matchee
cli/package.json#version npm publish --access public
📜 License
MIT. Ver LICENSE en el repo root.
Hecho con ❤️ por North Labs
Documentación completa · Framework manifest · Changelog del toolkit · npm package
