npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kikedealba/bita

v0.6.0

Published

Registro de tiempo local que documenta el trabajo y lo vuelca a Jira, con su integracion para Claude Code.

Readme

bita

Registro de tiempo local, en SQLite, pensado para que Claude Code lo lea y lo escriba. Mide el trabajo mientras ocurre y después lo vuelca a Jira: un issue por título y proyecto, un worklog por cada bloque medido, con estimación y cierre.

El nombre viene de bitácora.

Por qué existe

Antes esto hablaba con la API de Toggl Track. El plan gratuito tiene un límite horario de llamadas que bloqueó el trabajo tres veces en una sola sesión, dos de ellas a mitad de una escritura, dejando Jira por delante del registro de tiempo.

Casi toda la complejidad del CLI servía para rodear ese límite, no para resolver el problema: el throttle, los reintentos, la paginación, el caché de 24 horas, el espejo del cronómetro en curso y los tags usados como estado porque no había dónde guardarlo. Con una base local todo eso desaparece.

Quedan dos ventajas que no se buscaban:

  • Varios cronómetros a la vez. El límite de uno era de Toggl. Aquí un cronómetro corriendo es una fila con stopped_at nulo, y puede haber las que hagan falta.
  • El estado es una clave foránea. Una entrada está pendiente mientras no tenga fila en jira_links. No hay tag que pueda diverger ni retaggeo a medias.

Requisitos

Node 24 o superior. No es negociable: bita usa node:sqlite y el borrado de tipos nativo, así que desde un clon corre los .ts sin compilar. No hay dependencias de runtime ni bundler.

El paquete de npm sí lleva JavaScript compilado, y no por gusto: node se niega a borrar tipos en archivos bajo node_modules, sin bandera que lo levante. El build es un tsc que sólo borra los tipos —erasableSyntaxOnly está activo— y reescribe las extensiones de los imports.

node -v    # debe decir v24 o más

Instalación

Desde npm

npm install -g @kikedealba/bita
bita setup
bita app install

bita setup enlaza la skill y los comandos de barra en ~/.claude apuntando al paquete instalado, y mete los permisos y el hook SessionStart en tu settings.json. Al actualizar el paquete se actualizan con él, porque son symlinks. bita app install descarga la última release del escritorio y la deja en /Applications.

Node 24 o más nuevo, por node:sqlite.

Cómo se publica

Nadie publica a mano. Al publicar una release en GitHub, el workflow .github/workflows/publish.yml corre el typecheck y las pruebas, comprueba que el tag y la versión de package.json coinciden —si no, falla antes de subir nada—, instala el tarball como lo haría una persona y lo ejecuta, y hace npm publish --provenance.

Ese último paso no es ceremonia: el paquete se instala en node_modules, que es un entorno en el que no corre lo mismo que en el clon. Comprobar el contenido del tarball no lo detecta; ejecutarlo sí.

La procedencia ata el paquete de npm al commit y al workflow que lo construyó, así que cualquiera puede comprobar de dónde salió. Hace falta el secreto NPM_TOKEN en el repositorio, un token de automatización con permiso de escritura sobre @kikedealba.

Desde el repositorio

Para trabajar sobre el código. El instalador enlaza contra tu clon, así que un git pull actualiza el binario, la skill y los comandos a la vez.

1. Clonar e instalar

git clone [email protected]:KikeDeAlba/bita-cli.git
cd bita-cli
pnpm install

Las únicas dependencias son TypeScript y @types/node, y solo para el typecheck.

2. Correr el instalador

./scripts/install.sh

Hace cuatro cosas, y todas son idempotentes: puedes volver a correrlo cuando quieras.

| Paso | Qué hace | |---|---| | Binario | Enlaza bita en tu directorio de binarios ($PNPM_HOME/bin, o ~/.local/bin) | | Skill | ~/.claude/skills/bita → skill/ del repo | | Comandos | ~/.claude/commands/bita-*.md → commands/ del repo | | Settings | Añade los permisos y el hook SessionStart a ~/.claude/settings.json |

Todo son symlinks al repo, a propósito: cuando actualizas el repo, la skill y los comandos se actualizan contigo, y un cambio de flag en el CLI viaja en el mismo commit que su documentación.

Antes de tocar settings.json deja una copia en settings.json.backup, y si no lo puede parsear no lo escribe: imprime el bloque para que lo pegues a mano.

Si tu directorio de binarios está en otro sitio:

BITA_BIN_DIR=~/bin ./scripts/install.sh

3. Comprobar

bita --version
bita projects

La base se crea sola en ~/.local/share/bita/bita.db al primer uso. BITA_DB_PATH la mueve a otro sitio, que es también la forma de probar cosas sin tocar la real. BITA_CONFIG_PATH hace lo mismo con la configuración (~/.config/bita/config.json).

4. Dar de alta un repositorio

Esto es lo que enciende la integración con Claude. Un solo comando crea el proyecto y lo mapea:

bita repo init                     # el repositorio actual
bita repo init ~/dev/otro/repo     # o el que le pases

El nombre del proyecto sale de la carpeta; --name "Otro nombre" lo cambia. Si ya existe un proyecto con ese nombre lo reutiliza, que es lo que quieres cuando el front y el back de lo mismo deben compartir proyecto.

Si prefieres separarlo en dos pasos, o mapear varios repositorios a un proyecto que ya existe:

bita project add "Mi proyecto"     # devuelve un id
bita scope set . <projectId>

Mientras un repositorio no esté mapeado, el hook no dice nada. En cuanto lo está, al abrir una sesión de Claude Code en él se inyecta la regla que le pide ofrecer el cronómetro cuando el trabajo vaya a dejar un artefacto —un commit, un archivo, un despliegue— y callarse cuando solo vayas a leer o preguntar.

El mapeo se guarda por el slug del repositorio, que sale del remoto de git (github.com/kikedealba/bita-cli), así que sobrevive a que muevas la carpeta.

Reabre la sesión de Claude Code para que cargue el hook, la skill y los comandos.

5. Conectar Jira

Jira no se toca desde el CLI: lo escribe Claude por el conector de Atlassian. Lo único que se guarda aquí es a qué tablero va cada proyecto, y se pregunta solo la primera vez:

bita map set <projectId> <JIRAKEY> --parent <JIRAKEY-123>   # siempre a esa épica
bita map set <projectId> <JIRAKEY> --no-epic                # al tablero: la épica se elige en cada corrida
bita map list

Un proyecto apunta a una épica cuando todo su trabajo cae siempre en la misma, o al tablero cuando se reparte entre varias. Cambiar de uno a otro conserva el resto del mapeo: la transición de cierre, los tipos y las Historias ya creadas, que se guardan por épica.

Uso

bita start "Despliegue de infraestructura"   # arranca; puede haber varios
bita ls                                      # qué está corriendo ahora
bita note path 12 --create                   # el documento de la entrada
bita note save 12                            # regístralo tras editarlo
bita stop 12                                 # para uno y cierra su documento
bita log "Sesión con QA" --from 14:00 --for 1h
bita summary --pending --json                # agrupado y listo para Jira
bita link 12 13 --issue DD-1896              # marca como registradas
bita delete 12 --dry-run                     # qué se llevaría por delante
bita repo init ~/dev/otro/repo               # da de alta otro repositorio

bita --help lista todo.

Borrar

bita delete <ids...> quita entradas que nunca debieron registrarse: el contador que arrancó solo, el bloque de tres segundos, la prueba. Se lleva consigo el enlace a Jira, los archivos tocados, la fila del documento y el archivo del documento en disco, salvo con --keep-doc.

Hay tres guardas, y todas paran la corrida entera antes de tocar nada:

| Situación | Qué pasa | |---|---| | La entrada sigue corriendo | Se niega y remite a bita cancel, que es el comando de descartar un cronómetro vivo | | La entrada ya llegó a Jira | Se niega: el worklog sigue allá y el conector no puede borrarlo. --force borra la entrada local de todos modos | | El id no existe | Se niega antes de borrar ninguno de los otros |

Sin terminal —o con --json— exige --yes, porque no hay a quién preguntarle. --dry-run describe lo que pasaría, incluido que se negaría, y no escribe nada.

bita project delete <id|nombre> hace lo propio con un proyecto: borra su mapeo de Jira, sus Historias cacheadas y los prefijos de scope que apuntaban a él. Se niega si el proyecto tiene entradas, porque borrarlo las deja sin proyecto en vez de borrarlas; --force acepta ese resultado y bita project archive es la alternativa cuando el histórico importa.

Los documentos

Cada entrada tiene un documento en markdown que se escribe mientras el cronómetro corre, no al pararlo. Viven en espejo del proyecto:

~/.local/share/bita/
├── bita.db
└── docs/
    └── apartados/2026/09/20-128-migracion-del-worker.md

La raíz sale de --docs-dir, de BITA_DOCS_DIR, o del directorio de la base de datos, en ese orden. Como se deriva de la base, apuntar --db-path a un archivo de pruebas arrastra los documentos con él.

La base guarda la ruta, no el texto. El archivo es el original: se puede abrir en un editor, indexar o respaldar sin pasar por el CLI, y el árbol entero se puede mover sin reescribir nada. Lo que sí guarda la base es el checksum, con lo que se nota si un documento cambió por fuera.

Las secciones son fijas —Contexto, Qué se hizo, Decisiones, Hallazgos, Verificación, Pendiente, Tocado— porque son la superficie sobre la que se construye la descripción del issue de Jira y, más adelante, una página de Confluence.

Si vienes de las notas en NDJSON, bita notes migrate --dry-run enseña qué documentos se crearían, y sin el flag los crea. El archivo viejo no se toca.

Navegar lo escrito

bita note siempre habla de una entrada concreta. Para moverse por el corpus —que es lo que necesita un lector, dentro o fuera de la terminal— está bita docs, que solo lee:

bita docs tree --months                      # proyectos, con sus meses y conteos
bita docs ls --project ARSM                  # entradas y su documento, o «sin nota»
bita docs show 735                           # markdown, front matter y secciones
bita docs search "cognito" --project ARSM    # con fragmentos alrededor de cada acierto

docs ls devuelve siempre las siete secciones con su estado —written, empty o absent— para que quien pinte un índice no tenga que llevar su propia copia de la lista. Las entradas sin documento salen como filas con doc: null, porque no tener nota escrita también es información.

Un archivo que falta o que cambió por fuera no es un error: sale en meta.files y en meta.warnings con ok: true. Que el documento vaya por delante de la base entre note path --create y note save es el flujo normal, no una avería.

La búsqueda lee de disco, acotando antes por la base: el catálogo dice qué archivos existen y el archivo dice qué contiene. No hay índice que invalidar, y buscar dentro de un proyecto solo toca los documentos de ese proyecto.

Formato de salida

Todos los comandos aceptan --json y emiten un solo documento en stdout:

{ "schemaVersion": 3, "ok": true, "command": "summary", "data": {}, "meta": {} }

Los errores salen con ok: false y un error.code estable. Los avisos van a stderr, nunca a stdout, para que el JSON se pueda parsear tal cual.

Contadores en blanco

El caso normal es arrancar el reloj antes de saber en qué se trabaja:

cd ~/dev && bita start        # sin título y sin proyecto

Eso crea un borrador. Mientras siga sin título queda fuera de summary, así que no puede llegar a Jira por accidente. Se rellena después, y en buena parte solo:

| Qué | Quién lo pone | |---|---| | Título y descripción | Claude, en cuanto un mensaje dice en qué se va a trabajar | | Proyecto | Claude por el prompt, o el hook por el primer archivo que se cambia | | Archivos tocados | El hook, en cada edición |

El hook UserPromptSubmit recuerda que hay un contador sin nombre y se calla solo en cuanto lo tiene. A mano:

bita amend --draft --title "Lo que sea" --project Apartados

Proyectos y repositorios

Un repo no es un proyecto. Los proyectos suelen ser grupos con varios repos dentro, y el grupo no tiene .git: lo tienen los repos.

El mapeo va por prefijo de ruta, y gana el más largo que empate:

bita scope set gitlab.com/vivaaerobus/vb_solemti/apartados 42
bita scope which .        # que prefijo empata aqui
bita scope list

Con eso, apartados/api, apartados/front y apartados/workers resuelven los tres a Apartados sin configurar nada mas. Se puede mapear un grupo y luego excepcionar un repo dentro, porque el prefijo mas largo manda. El empate es por segmentos, asi que .../apartados nunca cubre .../apartados-legacy.

Si no hay prefijo, bita repo init propone uno comparando los segmentos de la ruta con los nombres de proyecto que ya existen, ignorando mayusculas, guiones y guiones bajos. Solo empata si tras normalizar son identicos.

Cómo se agrupa

Un grupo es proyecto + título, a lo largo de todo el rango, y se convierte en un issue de Jira. Cada entrada del grupo es un worklog con su hora real.

La estimación original se redondea hacia arriba al siguiente medio punto: 3h 43m medidas se registran como 4h de estimación con worklogs que suman 3h 43m. Un issue admite como máximo 8 horas; lo que se pasa se parte en (1/n), (2/n).

Solapes

Los cronómetros simultáneos están permitidos, así que un día puede sumar más tiempo del que marca el reloj. summary y entries lo avisan:

Warning: 2026-09-19: 4h 8m tracked over 3h 7m of clock time (1h overlapping)

No lo impide. Solo evita que pase inadvertido.

Los comandos de Claude Code

commands/ tiene siete slash commands, enlazados por symlink desde ~/.claude/commands/:

| Comando | Qué hace | |---|---| | /bita-start [título] | Arranca un cronómetro. Sin título, lo infiere de la sesión y lo enseña antes | | /bita-stop [id] | Cierra el documento de lo que se hizo y para. Con varios abiertos, pregunta cuál | | /bita-timers | Qué está corriendo y cuánto llevas hoy | | /bita-log <texto> | Registra un bloque que ya pasó, cuando se trabajó sin cronómetro | | /bita-init [ruta] | Da de alta un repositorio: crea su proyecto, lo mapea y revisa el tablero | | /bita-check [id] | Anota un checkpoint en el documento, mientras el cronómetro corre | | /bita-amend [id] | Rellena a mano el título o el proyecto de un cronómetro |

Viven en el repo por la misma razón que la skill: usan los flags del CLI, así que cambian en el mismo commit.

La skill

skill/SKILL.md es la skill de Claude Code que envuelve el CLI: decide cuándo proponer el cronómetro, infiere la Historia de Jira a partir de las notas, y maneja la jerarquía Épica → Historia → Subtarea. Está enlazada por symlink desde ~/.claude/skills/bita, para que el procedimiento y los flags cambien en el mismo commit.

Desarrollo

pnpm typecheck
pnpm test

Los tests corren con node --test sobre los .ts directamente. No hay bundler.

Estructura

src/db/        el almacén: esquema, migraciones y consultas
src/domain/    lógica pura: agrupación, duraciones, zonas horarias, solapes
src/cli/       comandos y formato de salida
src/docs/      los documentos de cada entrada: rutas, markdown y escritura
src/state/     configuración y notas heredadas en disco
skill/         la skill de Claude Code
commands/      los slash commands
scripts/       el instalador

El punto de corte es EnrichedTimeEntry (src/domain/types.ts): todo lo que está aguas abajo no sabe de dónde salieron los datos.