Herramienta de línea de comandos · Go · un binario estático

$ dharness

Una puerta de commit para proyectos TypeScript.

Es dueño de la invocación de ESLint, react-doctor, fallow y Stryker: qué herramienta corre, en qué orden, acotada a qué y con qué techo de recursos. El veredicto es su código de salida, propagado sin tocarlo.

4
comandos
11
pasos que deriva
4
etapas en la puerta
~/projects/your-appRuta ocultada
$ dharness sync dharness 1.7.5 · sync · ~/projects/your-app   js project       repository root  package manager  yarn  test runner      vitest  owned files      .dharness/ ■ 11 steps · 5 applied · 3 delegated · 3 satisfied · 0 failed   6.76s ── Applied (5) ──  ✓ 1/11 install what this project is missing              6.73s         │ ➤ YN0013: │ 33 packages were added to the project.         │ installed [email protected]         ~ package.json         ~ yarn.lock ✓ 2/11 write the files dharness owns                     0.03s         + .dharness/lefthook.yml         + .dharness/fallow.jsonc         + .dharness/eslint.config.mjs         + .dharness/rules.json ✓ 3/11 point .fallowrc.json at the file dharness owns    0.00s ✓ 6/11 point eslint.config.js at the file dharness owns  0.00s ✓ 8/11 give the agent fallow’s own tools                 0.00s ── Left to you (3) ──  ! 9/11   wire the gate into git   nothing answers: there is no lefthook config, no .husky/ and      no lefthook binary. Choosing a hook manager is a decision      this project has not made, and not a default dharness gets      to pick. … steps 10, 11 and the satisfied three elided … ✓ 5 applied · 3 delegated · 3 satisfied · 0 failed   exit 0   next  wire the gate into git
Salida real. `sync` detectó yarn y vitest por su cuenta, derivó once pasos, aplicó los cinco que podía y nombró los tres que devolvió. Se sustituyeron la ruta del proyecto y la cadena de versión y se omitió un bloque, las tres cosas señaladas; el resto de los caracteres son de la herramienta.
La puerta

Lo barato primero, y la primera falla corta el resto

Una falla detiene la ejecución y omite todas las etapas que vienen atrás, y en una puerta que corre en cada commit ese corte es la mayor parte del ahorro. Cada etapa de abajo lleva la mediana con la que se midió y lo que la explica.

  1. 011008 ms
    ESLint--no-warn-ignored <staged files>

    Lint sobre la lista exacta del índice, resuelto localmente

    Se ejecuta desde la versión que tu proyecto instaló, sobre la lista exacta del índice y con la configuración que dharness posee, que ya trae el preset que tu framework recomienda.

    Por quéSe resuelve desde la instalación que tu proyecto ya tiene, sin viaje al gestor de paquetes.

    Corre en cuanto tu proyecto lo instala

  2. 022959 ms
    react-doctor--staged --no-dead-code --no-score --no-supply-chain -y

    Semántica de React, solo sobre el cambio en el índice

    Acotado a lo que realmente se está confirmando, y es la única etapa aquí de la que eso es cierto. Su análisis se queda en tu máquina.

    Por quéAcotado al cambio en el índice, así que el costo sigue al diff.
  3. 032102 ms
    fallow auditaudit --changed-since HEAD --diff-stdin

    El grafo del repositorio, juzgado por lo que el cambio introduce

    El diff del índice se le entrega por stdin, así que el grafo se juzga sobre el cambio que estás por confirmar. Sale con 1 ante un veredicto de fallo y limita la puerta a los hallazgos nuevos.

    Por quéConstruye el grafo del repositorio de todas formas, así que la mediana tiene un piso.

    Corre en cuanto el repositorio tiene un commit contra el cual comparar

  4. 041398 ms
    fallow dupesdupes

    El techo de duplicación, aplicado sobre todo el repositorio

    El techo vive en la configuración que dharness posee, donde tu proyecto puede leerlo y sobrescribirlo.

    Por quéConstruye el mismo grafo y luego hace una sola pregunta a todo el repositorio.

    Corre en cuanto el repositorio tiene un commit contra el cual comparar

Medianas de tres ejecuciones cada una, un proyecto de referencia y la misma lista explícita de archivos preparados para cada etapa. 12 de agosto de 2026.

Primera fallaexit 1
El mismo repositorio, dos preguntasfallow 3.14.0 · un repositorio con 80% de duplicación · umbral 3
  • fallow audit

    ¿Qué introduce este cambio?

    exit 0
  • fallow dupes

    ¿Cuánta duplicación carga el repositorio?

    exit 1
El veredicto

El código de salida es la respuesta, propagado sin tocarlo. Una etapa que falla nombra todas las que omitió, así que siempre se rinde cuenta de las cuatro.

~/projects/your-app
$ dharness checkno staged source files, nothing to check $ echo $?0
Salida real, y la ejecución más barata posible: sin fuentes en el índice no se levanta ninguna de las cuatro etapas. La misma salida temprana aplica a la mutación, donde un alcance vacío nunca arranca el motor.
La superficie

Cuatro comandos, y a cada uno se le puede poner nombre

Toda la superficie cabe en esta pantalla.

  1. $ dharness sync

    Deja el proyecto listo, y lo mantiene así

    Escribe

    Cada ejecución deriva su plan completo del repositorio tal como está ahora mismo, así que ejecutarlo meses después reporta la deriva.

    Banderas
    --format <human|json> (default human)
    Lo que hace una ejecución, en orden
    1. 01Instala lo que falta
    2. 02Escribe en .dharness/ los archivos que posee
    3. 03Apunta tu configuración a ellos con una línea de referencia
    4. 04Cablea la puerta en git
    5. 05Entrega al agente lo que no puede ejecutar, con el motivo
    Probado antes de que la ejecución pueda reportar éxitoeslint --print-config

    Una ruta por cada extensión de fuente, hasta ocho. Un sync en verde es uno cuya configuración ya se cargó y se releyó.

    Una ejecución fallida deja el repositorio como lo encontró
    Lo que hace la ejecuciónLo que deja un fallo
    Paquetes instalados, archivos escritos en .dharness/Se quita exactamente lo que agregó esta ejecución
    El manifiesto y el lockfile editadosAmbos restaurados byte por byte
  2. $ dharness check

    La puerta de commit propiamente dicha

    No escribe nada

    Se detiene en la primera falla, nombra todas las etapas que omitió y te entrega la ayuda de esa misma herramienta, en la forma que usa tu proyecto. También nombra qué tipo de falla obtuvo: un código de salida 2 de ESLint es una configuración que nunca cargó, así que no se analizó ningún archivo y la respuesta está en la configuración.

  3. $ dharness mutate <path...>

    Averigua si las pruebas de estos archivos notarían que el código se rompe

    No escribe nada

    Una ruta puede nombrar líneas en lugar del archivo entero, como `src/thing.ts:12-40`, y el veredicto cubre exactamente esas líneas. Ejecuta el Stryker que tu proyecto instaló, el único capaz de resolver tu propio TypeScript.

    Banderas
    --dry-run
    --concurrency <n> (default 2)

    Cada herramienta calcula su paralelismo sobre la máquina entera, y más workers se midieron más lentos en un alcance pequeño.

    --upgrade
    --fresh

    Los resultados se acumulan en `.git/dharness/`, así que la tabla de Stryker puede cubrir más archivos de los que pediste. Esta bandera mide solo las rutas que nombraste.

  4. $ dharness version

    Imprime la versión

    No escribe nada

    Las versiones las corta release-please a partir de los commits convencionales en main, así que el número que imprime nombra una versión etiquetada con una entrada de changelog detrás.

Propiedad

Una pregunta por herramienta, hecha una sola vez

Cuatro herramientas que ya existen, cada una dueña de un diagnóstico propio.

  • ESLint

    ¿Este archivo sigue las reglas que recomienda este stack?

    • El preset que documenta tu framework
    • Cuatro presets: Next.js, Expo, Wails, genérico
    • Resuelto desde tu propia instalación
    • Ejecutado sobre la lista explícita del índice
    El preset que nombra la guía de cada framework
    FrameworkPreset
    Next.jseslint-config-next/core-web-vitals
    Next.js, en cuanto existe un tsconfig.jsoneslint-config-next/typescript
    Expoeslint-config-expo/flat.js

    La versión sale del framework que publica el preset. Tu `eslint.config.js` gana una sola línea de referencia, dentro de una región marcada que se reescribe en cada sync, apuntando a `.dharness/eslint.config.mjs` o `.cjs`, según tu proyecto.

    Visitar la herramienta
  • fallow

    ¿Cómo es el grafo de este repositorio?

    • Código muerto
    • Dependencias
    • Ciclos
    • Fronteras de arquitectura
    • Entry points, reportados directamente
    Qué cuenta como cruzar una zona
    ImportaciónEn sus fronteras
    Una importación solo de tiposCruza la zona
    Una importación de un paquete externoNo cuenta como cruce

    Imprime los entry points que resolvió y avisa cuando una zona no coincide con nada.

    Visitar la herramienta
  • react-doctor

    ¿Este código React es semánticamente correcto?

    • Semántica de React y React Native
    • Mal uso de hooks y efectos
    • Deriva del sistema de diseño
    • Acotado al cambio preparado en la puerta
    • Integrado en ESLint como plugin

    Llega al proyecto por dos caminos: la CLI en la puerta y `eslint-plugin-react-doctor` en la configuración que dharness posee, así que el mismo análisis responde mientras escribes y otra vez en el commit. La severidad de sus reglas solo acepta `error`, `warn` u `off`, y esa única restricción produjo el paquete complementario de abajo.

    Visitar la herramienta
  • Stryker

    ¿Las pruebas notarían que el código se rompe?

    • Pruebas de mutación sobre una lista acotada de archivos
    • Alcance intersectado con el cambio
    • Concurrencia con techo de 2 por defecto
    • Se ejecuta deliberadamente, fuera de la puerta

    El veredicto sale del reporte que escribe, así que el número que decide una ejecución es uno que puedes leer.

    Visitar la herramienta
El paquete complementario

Tamaño, documentación y forma de carpetas, verificados

dharness-eslint-plugin

El tamaño, la documentación y la forma de las carpetas necesitan un número o una política contra la cual verificarse, y este paquete es donde un proyecto la declara.

Las seis reglas que publica
  • dharness/max-file-linesUn archivo que pasa el techo que fijó este proyecto.
  • dharness/require-jsdocUna declaración en el tope de un archivo sin nada que diga para qué sirve.
  • dharness/require-variable-jsdocUna variable de nivel superior sin JSDoc inmediatamente encima.
  • dharness/pure-index-barrelUn barrel que hace algo distinto de re-exportar.
  • dharness/role-file-shapeUna declaración que el nombre del archivo de rol no prometía.
  • dharness/folder-ownershipUna carpeta que parte un módulo en archivos de rol y no publica un index.dharness pregunta `git ls-files -- */index.ts */index.tsx` y escribe esta regla como `error` donde el árbol ya publica un barrel, y como `off` donde no.
Dónde viven los números
{
  "schema": "dharness.rules/v1",
  "maxFileLines": 500,
  "roleSuffixes": [".types.ts", ".constants.ts", ".helpers.ts", ".schema.ts"]
}

Los números viven en `.dharness/rules.json`, así que un proyecto puede diferir de otro sin publicar una versión nueva. Un archivo ausente o ilegible cae a los valores por defecto, y todas las demás reglas siguen corriendo.

De dónde lee su respuesta cada regla
  • dharness/max-file-linesmaxFileLines
  • dharness/folder-ownershiproleSuffixes
Un solo registro, todos los archivos que ESLint toca

Un único registro `plugins: { dharness: plugin }` en la configuración que dharness posee es todo el cableado, y pone las seis reglas en cualquier lugar donde ESLint ya se ejecute en ese proyecto. El paquete publica una entrada CommonJS y otra ESM, así que carga en cualquiera de los dos sistemas de módulos.

Ver el código
Instalación

Un comando, desde la terminal en la que ya estás

Un único binario estático para Linux, macOS y Windows, en amd64 y arm64.

$ go install github.com/Disble/dharness/cmd/dharness@latest
Abrir el repositorio
  • Disble/dharness

    La CLI en sí: su código, sus versiones etiquetadas y la bitácora fechada.

  • dharness-eslint-plugin

    Las seis reglas que ESLint carga desde la configuración que dharness posee, publicadas con una entrada CommonJS y otra ESM.

  • ditto

    El motor de mutación en Go con el que este repositorio revisa sus propios cambios preparados.