8000 Releases · habit-hooks/habit-hooks · GitHub
[go: up one dir, main page]

Skip to content

Releases

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 05 Sep 17:04

Ruby. A Gemfile or a .rubocop.yml in your project and habit-hooks now
coaches your Ruby the way it already coached your Python and TypeScript.

Added

  • The ruby plugin. pip install "habit-hooks[ruby]", add "ruby" to
    plugins, and a rubocop sensor translates cop names into the same smell
    vocabulary every other plugin speaks: too-many-parameters,
    oversized-function, oversized-block, high-complexity, deep-nesting,
    unused-variable, swallowed-exception and parse-error. All three Metrics
    complexity cops —
    CyclomaticComplexity, PerceivedComplexity and AbcSize — coach as
    high-complexity, since a nesting-heavy or call-heavy method can trip one
    without the others.

    Your .rubocop.yml decides everything. The sensor passes no --only and no
    --config, so RuboCop discovers your config by its own upward walk and your
    habit-hooks run is the run you get from RuboCop directly. A cop this plugin
    has no smell for is forwarded under its own name rather than dropped, because
    a cop only fires if your config switched it on. --force-exclusion is the one
    flag the sensor adds, so AllCops: Exclude: keeps applying once habit-hooks
    names files on the command line.

  • A detector can name the directories its own tool lives in, through
    search_paths in a plugin's config.toml. The ruby plugin's rubocop detector
    names bin, where bundle binstubs writes binstubs, and for Ruby that matters
    more than a version difference usually does: a .rubocop.yml naming
    rubocop-rails or rubocop-rspec cannot be read at all by a rubocop outside
    the bundle those gems live in. It stays off the default search path, which
    every tool shares, because bin is a name a project may keep its own scripts
    under.

  • Java reports deep-nesting. PMD's AvoidDeeplyNestedIfStmts, pinned to
    problemDepth=3 in the bundled ruleset, so the third nested if is the first
    one reported. A project running its own PMD ruleset gets the smell only when
    that ruleset enables the rule itself. Contributed by @renanfranca (#160).

Fixed

  • A wrapped tool that never started no longer reads as a clean file. The
    rubocop binstub begins #!/usr/bin/env ruby, so pointing it at an
    interpreter it is not installed into makes it die in find_spec_for_exe and
    exit 1 — the code that otherwise means "I found offences". The sensor judges
    the report rather than the exit code: RuboCop prints its JSON envelope on
    every run it completed, so no envelope means no run, and the run fails instead
    of reporting nothing wrong.

  • An uncoached smell no longer coaches in another language. A smell no
    plugin ships a guide for falls back to the uncoached guide, and that
    fallback was resolved across every active plugin rather than the ones the
    finding's own language names — so a project running two language plugins
    could be handed the wrong language's wording for its own file. The fallback
    now follows the same language-filtered order every other guide does.

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 27 Aug 20:58

Native Windows support, plus everything else the two Windows reporters found on
the way — none of which is a Windows bug; all of it reproduces on any platform.
Reported by @FrankRaiser (#133, #140, #143) and @imalliaras (#134, #142),
neither of whom could get a run to complete when they started.

Fixed

  • habit-hooks runs on native Windows. Every sensor was spawned through
    bash -c, which Windows has not got — and where bash does resolve there it
    is usually C:\Windows\System32\bash.exe, the WSL launcher, which answers
    about a different filesystem. Nine of the eleven shipped sensors and
    transformers never needed a shell; the other two only needed one to reach
    jq. All eleven now spell argv and are spawned directly.

  • Nothing decodes in the console's codepage any more. Every guide, config
    and spec file this tool reads is UTF-8 and now says so. On a cp1252 console
    the first guide it rendered killed the run with a UnicodeDecodeError (#133);
    the same bug hit any non-UTF-8 locale, including LC_ALL=C on Linux. The
    streams it writes are UTF-8 too, so a printed block's box-drawing characters
    cannot fail on the way out.

  • A tool installed as .cmd or .bat is found. Windows appends only .exe
    to a bare command name, so jscpd.CMD, knip.cmd and pmd.bat were cleared
    at setup and unreachable at run time. One lookup now answers both questions.

  • init no longer offers an install it cannot run. Its install command went
    through shell=True, which on Windows is cmd.exe, and cmd.exe does not
    understand the quotes in uv tool install 'habit-hooks[python]'.

  • A wedged sensor still says why. Windows raises its timeout carrying no
    output at all, so the notice quoted back nothing where the tool had explained
    itself.

  • A mistyped argv in a sensor spec answers with a sentence. argv = "ruff"
    is a string, and a string is iterable: it spawned four one-character arguments
    and the run reported needing the r command. A non-string element raised a
    traceback. Both are now refused by name at exit 2 — the code for a broken
    tool — instead of exit 1, the code reserved for a finding to fix.

  • A sensor no longer dies without a word when its tool has a lot to say.
    Node caps a captured stream at 1 MB unless told otherwise, and the eslint
    sensor never told it otherwise: past that, the spawn came back with no exit
    status, a truncated report and an empty stderr, and the sensor forwarded the
    empty stderr. The whole notice was a command line. Around 100 files with
    findings is enough to cross it, on any platform (#142). What a project tool's
    run answered — how much of it there may be, whether it broke, and what to say
    about it — is now one seam's question rather than each caller's, so the two
    callers cannot answer it differently again. A complaint can no longer be empty.

  • --all no longer scans what git ignores. Every git-derived mode asks git
    and so had never seen a build artifact; a whole-project run walked the
    directory tree and measured everything it found. One project, two universes:
    a real pnpm monorepo held 321 tracked .ts/.tsx files and 843 on disk, and
    its owner had to hand-write !**/dist/** into [files] before a run was
    usable (#142). A project git cannot answer for — outside a repository, on a
    machine with no git, or one that is itself ignored by the repository around it
    — still measures what is on disk, because a run that silently scans nothing is
    worse than one that over-scans. A submodule's files leave the scope, as they
    always had from every git-derived mode, and the run now says which submodule
    rather than quietly measuring less.

  • A scan that measured nothing says so. A [files] that matches no file —
    because it is too narrow, or because git ignores the tree it was written for —
    used to render the clean guide over a run that had read nothing at all.

  • A smell is coached once, not once per sensor that saw it. Two sensors can
    report one smell — eslint's max-lines and the line counter both mean
    oversized-file — and the mapper printed a block per finding, so the same
    ~200-word guide arrived twice (#140). Findings that resolve to the same guide
    are now one finding. A sensor's own issue list is never thinned: two unused
    variables declared on one line are still two.

  • habit-hooks no longer reports its own tools as unused dependencies. It
    asks a project to install eslint, knip, ts-morph, jscpd and the two
    @typescript-eslint packages, and then reported all of them as declared and
    imported nowhere (#143). The shipped knip config overlooks them. A project
    with its own knip config is unaffected, as it is for every other setting.

  • A failing tool's own words are bounded within a line, not only across
    lines.
    The notice quoted back at most 20 lines and any number of bytes, and
    eslint -f json is one line however many megabytes it holds — so a sensor
    that died mid-report put the whole report into a coding agent's context. Both
    ends of a long line now survive, with the middle named.

  • A [runners] command nobody installed answers in one line. Routing a smell
    to an executable guide instead of a Markdown one, and naming a command that is
    not there, printed a Python stack trace — the same first-contact mistake #114
    answered everywhere else, in the one place that sweep did not reach.

  • The knip sensor reports a column under the name the contract gives it.
    knip spells it col; every other sensor and docs/sensor-interface.spec.md
    spell it column, so the position was invisible to anything asking by name.

Changed

  • jq is no longer a dependency of any plugin. The ruff and eslint
    sensors piped their tool through it; both are now helpers beside their spec,
    like every other sensor here. The only install hint we could offer was
    brew install jq.

  • A sensor spec may spell argv = [...]. It is spawned as written, with no
    shell, and it is the only form that runs on native Windows. command = "..."
    remains for a plugin needing syntax a list cannot carry, and is refused off
    POSIX rather than spawned into a shell that is not there.

  • A sensor spec may name the tool it wraps. ${detector:<name>} expands to
    the file this project runs for a tool the plugin declared in its detectors,
    so the sensor is handed a path rather than a name something has to look up
    again. Only the first word of a spawn was ever resolved, and no shipped sensor
    spells its wrapped tool there — each is a helper that spawns ruff, deptry,
    jscpd, pmd or php one process further in, which is exactly where a
    .cmd or .bat shim went unfound. A declared tool that is not installed is
    now answered before the sensor is spawned, so the notice naming it no longer
    depends on each plugin's helper producing that answer itself.

  • eslint and knip are found in the project, not on PATH. They are
    resolved from node_modules and run through Node directly, so a globally
    installed copy is no longer used — the shipped eslint config could never have
    worked with one, since it resolves its plugins from the project. init names
    npm install --save-dev eslint knip when they are missing.

  • An argument that cmd.exe would read as syntax is refused. A .bat or
    .cmd is run through cmd.exe, and sensor arguments are filenames out of
    whatever branch is checked out. The question is asked of every program those
    arguments reach, the tool a sensor's helper spawns included — which is where
    pmd.bat and npm's jscpd.CMD actually are.

Known limits

  • The executable documentation under docs/ is POSIX shell and is skipped on
    Windows (#137). The tool's own test suite runs there in full.

v1.4.0rc3

v1.4.0rc3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Aug 17:17

Reported by @imalliaras (#142) and @FrankRaiser (#140, #143). None of them is a
Windows bug: all three reproduce on any platform, and were found there only
because that is where these two were running.

Fixed

  • A sensor no longer dies without a word when its tool has a lot to say.
    Node caps a captured stream at 1 MB unless told otherwise, and the eslint
    sensor never told it otherwise: past that, the spawn came back with no exit
    status, a truncated report and an empty stderr, and the sensor forwarded the
    empty stderr. The whole notice was a command line. Around 100 files with
    findings is enough to cross it, on any platform (#142). What a project tool's
    run answered — how much of it there may be, whether it broke, and what to say
    about it — is now one seam's question rather than each caller's, so the two
    callers cannot answer it differently again. A complaint can no longer be empty.

  • --all no longer scans what git ignores. Every git-derived mode asks git
    and so had never seen a build artifact; a whole-project run walked the
    directory tree and measured everything it found. One project, two universes:
    a real pnpm monorepo held 321 tracked .ts/.tsx files and 843 on disk, and
    its owner had to hand-write !**/dist/** into [files] before a run was
    usable (#142). A project git cannot answer for — outside a repository, on a
    machine with no git, or one that is itself ignored by the repository around it
    — still measures what is on disk, because a run that silently scans nothing is
    worse than one that over-scans. A submodule's files leave the scope, as they
    always had from every git-derived mode, and the run now says which submodule
    rather than quietly measuring less.

  • A scan that measured nothing says so. A [files] that matches no file —
    because it is too narrow, or because git ignores the tree it was written for —
    used to render the clean guide over a run that had read nothing at all.

  • A smell is coached once, not once per sensor that saw it. Two sensors can
    report one smell — eslint's max-lines and the line counter both mean
    oversized-file — and the mapper printed a block per finding, so the same
    ~200-word guide arrived twice (#140). Findings that resolve to the same guide
    are now one finding. A sensor's own issue list is never thinned: two unused
    variables declared on one line are still two.

  • habit-hooks no longer reports its own tools as unused dependencies. It
    asks a project to install eslint, knip, ts-morph, jscpd and the two
    @typescript-eslint packages, and then reported all of them as declared and
    imported nowhere (#143). The shipped knip config overlooks them. A project
    with its own knip config is unaffected, as it is for every other setting.

  • A failing tool's own words are bounded within a line, not only across
    lines.
    The notice quoted back at most 20 lines and any number of bytes, and
    eslint -f json is one line however many megabytes it holds — so a sensor
    that died mid-report put the whole report into a coding agent's context. Both
    ends of a long line now survive, with the middle named.

  • A [runners] command nobody installed answers in one line. Routing a smell
    to an executable guide instead of a Markdown one, and naming a command that is
    not there, printed a Python stack trace — the same first-contact mistake #114
    answered everywhere else, in the one place that sweep did not reach.

  • The knip sensor reports a column under the name the contract gives it.
    knip spells it col; every other sensor and docs/sensor-interface.spec.md
    spell it column, so the position was invisible to anything asking by name.

v1.4.0rc2

v1.4.0rc2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Aug 12:11

Native Windows support. Reported by @FrankRaiser (#133) and @imalliaras (#134),
neither of whom could get a run to complete.

New in rc2, on top of everything 1.4.0rc1 shipped: a sensor spec names the
tool it wraps, the cmd.exe argument guard reaches the tool a helper spawns,
and a mistyped argv answers with a sentence.

Fixed

  • habit-hooks runs on native Windows. Every sensor was spawned through
    bash -c, which Windows has not got — and where bash does resolve there it
    is usually C:\Windows\System32\bash.exe, the WSL launcher, which answers
    about a different filesystem. Nine of the eleven shipped sensors and
    transformers never needed a shell; the other two only needed one to reach
    jq. All eleven now spell argv and are spawned directly.

  • Nothing decodes in the console's codepage any more. Every guide, config
    and spec file this tool reads is UTF-8 and now says so. On a cp1252 console
    the first guide it rendered killed the run with a UnicodeDecodeError (#133);
    the same bug hit any non-UTF-8 locale, including LC_ALL=C on Linux. The
    streams it writes are UTF-8 too, so a printed block's box-drawing characters
    cannot fail on the way out.

  • A tool installed as .cmd or .bat is found. Windows appends only .exe
    to a bare command name, so jscpd.CMD, knip.cmd and pmd.bat were cleared
    at setup and unreachable at run time. One lookup now answers both questions.

  • init no longer offers an install it cannot run. Its install command went
    through shell=True, which on Windows is cmd.exe, and cmd.exe does not
    understand the quotes in uv tool install 'habit-hooks[python]'.

  • A wedged sensor still says why. Windows raises its timeout carrying no
    output at all, so the notice quoted back nothing where the tool had explained
    itself.

  • A mistyped argv in a sensor spec answers with a sentence. argv = "ruff"
    is a string, and a string is iterable: it spawned four one-character arguments
    and the run reported needing the r command. A non-string element raised a
    traceback. Both are now refused by name at exit 2 — the code for a broken
    tool — instead of exit 1, the code reserved for a finding to fix.

Changed

  • jq is no longer a dependency of any plugin. The ruff and eslint
    sensors piped their tool through it; both are now helpers beside their spec,
    like every other sensor here. The only install hint we could offer was
    brew install jq.

  • A sensor spec may spell argv = [...]. It is spawned as written, with no
    shell, and it is the only form that runs on native Windows. command = "..."
    remains for a plugin needing syntax a list cannot carry, and is refused off
    POSIX rather than spawned into a shell that is not there.

  • A sensor spec may name the tool it wraps. ${detector:<name>} expands to
    the file this project runs for a tool the plugin declared in its detectors,
    so the sensor is handed a path rather than a name something has to look up
    again. Only the first word of a spawn was ever resolved, and no shipped sensor
    spells its wrapped tool there — each is a helper that spawns ruff, deptry,
    jscpd, pmd or php one process further in, which is exactly where a
    .cmd or .bat shim went unfound. A declared tool that is not installed is
    now answered before the sensor is spawned, so the notice naming it no longer
    depends on each plugin's helper producing that answer itself.

  • eslint and knip are found in the project, not on PATH. They are
    resolved from node_modules and run through Node directly, so a globally
    installed copy is no longer used — the shipped eslint config could never have
    worked with one, since it resolves its plugins from the project. init names
    npm install --save-dev eslint knip when they are missing.

  • An argument that cmd.exe would read as syntax is refused. A .bat or
    .cmd is run through cmd.exe, and sensor arguments are filenames out of
    whatever branch is checked out. The question is asked of every program those
    arguments reach, the tool a sensor's helper spawns included — which is where
    pmd.bat and npm's jscpd.CMD actually are.

Known limits

  • The executable documentation under docs/ is POSIX shell and is skipped on
    Windows (#137). The tool's own test suite runs there in full.

v1.4.0rc1

v1.4.0rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Aug 00:38

Native Windows support. Reported by @FrankRaiser (#133) and @imalliaras (#134),
neither of whom could get a run to complete.

Fixed

  • habit-hooks runs on native Windows. Every sensor was spawned through
    bash -c, which Windows has not got — and where bash does resolve there it
    is usually C:\Windows\System32\bash.exe, the WSL launcher, which answers
    about a different filesystem. Nine of the eleven shipped sensors and
    transformers never needed a shell; the other two only needed one to reach
    jq. All eleven now spell argv and are spawned directly.

  • Nothing decodes in the console's codepage any more. Every guide, config
    and spec file this tool reads is UTF-8 and now says so. On a cp1252 console
    the first guide it rendered killed the run with a UnicodeDecodeError (#133);
    the same bug hit any non-UTF-8 locale, including LC_ALL=C on Linux. The
    streams it writes are UTF-8 too, so a printed block's box-drawing characters
    cannot fail on the way out.

  • A tool installed as .cmd or .bat is found. Windows appends only .exe
    to a bare command name, so jscpd.CMD, knip.cmd and pmd.bat were cleared
    at setup and unreachable at run time. One lookup now answers both questions.

  • init no longer offers an install it cannot run. Its install command went
    through shell=True, which on Windows is cmd.exe, and cmd.exe does not
    understand the quotes in uv tool install 'habit-hooks[python]'.

  • A wedged sensor still says why. Windows raises its timeout carrying no
    output at all, so the notice quoted back nothing where the tool had explained
    itself.

Changed

  • jq is no longer a dependency of any plugin. The ruff and eslint
    sensors piped their tool through it; both are now helpers beside their spec,
    like every other sensor here. The only install hint we could offer was
    brew install jq.

  • A sensor spec may spell argv = [...]. It is spawned as written, with no
    shell, and it is the only form that runs on native Windows. command = "..."
    remains for a plugin needing syntax a list cannot carry, and is refused off
    POSIX rather than spawned into a shell that is not there.

  • eslint and knip are found in the project, not on PATH. They are
    resolved from node_modules and run through Node directly, so a globally
    installed copy is no longer used — the shipped eslint config could never have
    worked with one, since it resolves its plugins from the project. init names
    npm install --save-dev eslint knip when they are missing.

  • An argument that cmd.exe would read as syntax is refused. A .bat or
    .cmd is run through cmd.exe, and sensor arguments are filenames out of
    whatever branch is checked out.

Known limits

  • The executable documentation under docs/ is POSIX shell and is skipped on
    Windows (#137). The tool's own test suite runs there in full.

v1.3.1

Choose a tag to compare

@github-actions github-actions released this 19 Aug 20:12

Discoverability and documentation. No behaviour changes.

Changed

  • Every package now has a PyPI page. All six shipped with an empty one: no
    README, no keywords, no classifiers, no project links, no license. Each plugin
    gains a README of its own, and the core's README is now its long description.

  • The README says what the tool actually does. It had been wrong about six
    things, five of them for several releases:

    • A plain habit-hooks run scans every file, not your branch's changes.
      [scope] autoBranchOffMain defaults to false, and init writes no
      [scope] block.
    • init writes plugins and nothing else. The config the README showed added
      files = ["**/*.py"], which replaces the python plugin's exclusions
      wholesale — so --all then walked the reader's .venv.
    • A language plugin's guide wins over generic's wherever generic sits in
      plugins, so ordering it last was never load-bearing.
    • The override chain resolves to installed package data, not to a sibling
      plugins/ directory.
    • The sample output was invented. It is now a real run.
    • test-only-dead-code was missing from the enforced list.
  • Onboarding says how to start on a codebase that already has smells.
    habit-sensors --all | habit-snooze --snooze now appears where the first run
    happens, rather than two hundred lines below it.

  • docs/config.md's [scope] table gains a Default column. Both its
    examples set autoBranchOffMain = true while the default is false, and
    neither said so.

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 15:12

Java. pom.xml or build.gradle in your project and habit-hooks now coaches
your Java the way it already coached your Python and TypeScript.

Added

  • The java plugin. pip install "habit-hooks[java]", add "java" to
    plugins, and a pmd sensor translates PMD 7's rule names into the same
    smell vocabulary every other plugin speaks: too-many-parameters,
    high-complexity, oversized-function, unused-variable, unused-import
    and swallowed-exception. PMD exits 4 when it finds violations and 5 when it
    cannot parse a file, so the sensor tells those apart — a crashed PMD fails
    the run rather than reporting clean.

    Your own PMD ruleset wins. PMD never looks one up by itself, so the
    sensor does: a --rulesets you pass through [sensors.pmd] args first, then
    the conventional places a Java project keeps one
    (src/main/resources/pmd/ruleset.xml, pmd/ruleset.xml, ruleset.xml,
    pmd.xml). The ruleset the plugin ships is only the answer to "this project
    has none". Any other PMD flag — --aux-classpath, --minimum-priority —
    goes through args to PMD untouched.

    Generated code is left alone: Maven's target/ and Gradle's build/ are
    excluded from what the plugin calls source. habit-hooks init recognises a
    Java project and names pmd among the tools to install.

    With thanks to @pfichtner, who wrote it.

Changed

  • The too-many-parameters guide names the concept, not the bag. The
    default fix for a long parameter list is an options object named after the
    function that takes it — the report clears and the missing abstraction stays
    unnamed. The guide now says to search wider than the file that fired, because
    values that keep appearing side by side are usually one of the domain's own
    nouns; to use that entity everywhere it fits, since a call passing three of
    its fields is the same concept under the threshold; and that a FooProps is
    the { ...everything } bag under another name.

Fixed

  • A Python project that declares no dependencies could never get a clean
    run.
    With no pyproject.toml and no requirements.txt there is nothing for
    deptry to check, and it says so by aborting — which the sensor read as a
    broken tool. Every run came back as an incomplete one, and no change to the
    code could make it green. A project that declares no dependencies has no
    unused ones, so the sensor now reports nothing and the run completes. Every
    other way deptry can fail still fails loudly.

  • A broken tool was quoted back by the half of its output with nothing in
    it.
    Past twenty lines, only the first twenty were shown — but a Python
    traceback names what went wrong on its last line, so every sensor that
    wraps a tool in Python answered a crash with framework internals and never
    the error itself. Both ends are quoted now and the middle is counted, so the
    diagnosis survives whichever end it landed on. The incomplete-run message no
    longer promises a fuller diagnosis on stderr either, which was never there.

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 14 Aug 17:14

Fixed

  • Upgrading with pip left the plugins behind. pip install -U habit-hooks upgrades a dependency only when the new core stops being
    satisfied by the installed one, and the core accepted any 1.x plugin — so an
    upgrade to 1.2.0 gave you the new core with 1.1.0 plugins, and none of that
    release's fixes, nearly all of which are in the plugins. Each plugin is now
    floored at the core's own minor, and a test keeps the floor moving with it.
    uv pip install --upgrade, uv tool upgrade and brew upgrade were never
    affected; if you upgraded with pip already, upgrading again is enough.

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 14 Aug 16:53

Everything here is about the first ten minutes. Installing habit-hooks was the
most frequent question the project got, and working through one user's report
found that three of the steps they had to figure out for themselves were our
bugs, not their setup.

Added

  • habit-hooks init. It detects the language your project is written in, writes
    .habit-hooks/config.toml enabling the plugins it needs, and lists what is still
    missing — the plugins themselves, and the tools they reach for — beside the
    command that installs each, offering to run them. The install command it prints matches how habit-hooks was
    installed, so a plugin cannot land in some other Python than the one habit-hooks
    runs from.

    Run it again on a project that already has a config and it changes nothing, only
    reporting what is missing — so the same command answers "why is this run not
    reporting anything?". For a language habit-hooks has no plugin for, it prints the
    prompt to hand to your coding agent to build one.

  • A plugin declares the tools it needs and how to install them. Each plugin's
    config now carries a detectors list — the tool's name, whether it is a command
    on PATH or a package read as a library, and the command that installs it. A
    third-party plugin gets this without the core learning its vocabulary.
    Writing it down found two requirements nothing had ever stated: the typescript
    plugin needs node itself, and the python plugin needs jq, which its ruff
    sensor has always piped through.

Fixed

The first ten minutes

  • The typescript plugin's comment sensor now looks for ts-morph in your project
    rather than beside itself, so it no longer dies with a Node stack trace on every
    install that puts habit-hooks outside your repository — pip, uv tool and
    Homebrew alike. A project that has not installed ts-morph is told to, in one
    line, instead of being shown the module loader's.

  • ts-morph is listed among the typescript plugin's detectors in the README,
    which never mentioned it.

  • A TypeScript project with no eslint config of its own is linted with the config
    habit-hooks ships — which needs typescript-eslint in the project, something
    neither the README nor the error said. The config now names itself and what to
    install instead of failing with a module-loader stack trace, and the README
    lists it.

  • A knip nobody installed is named the way jscpd, deptry and php already
    were — "install it, or disable the sensor" — instead of Error: spawnSync knip ENOENT. It was the one detector spawned from Node, and the only one still
    missing that answer.

  • A plugin's declared files reached into your dependencies. A project that
    names no files of its own scans what its plugins declare, and habit-hooks init writes exactly such a config — so a bare **/*.ts made the first run
    report on node_modules. On a small TypeScript project that was 23,573 findings
    in other people's packages hiding the eight that were the project's own. The
    typescript plugin now excludes node_modules (nested copies in a monorepo
    too), python excludes site-packages and a virtualenv under either of the two
    names one usually goes by, and php excludes vendor. An exclusion holds for
    every active plugin, so a project running two languages cannot have one
    plugin's globs hand back what another just excluded.

  • Every plugins example listed generic first, which is the wrong way round:
    the list is a lookup priority and generic is the fallback, so listing it first
    makes a language plugin's own guides unreachable — the python plugin ships
    high-complexity and swallowed-exception guides exactly because those want a
    Python answer, and neither could ever fire. The prose always said "falling back
    to generic last"; only the examples disagreed. habit-hooks init writes the
    right order.

  • Naming two uv tool install extras one after another loses the first. Each
    one rebuilds the environment rather than adding to it, so following the README's
    [python] line and then its [typescript] line left typescript alone — and a
    Python project quietly stopped being checked. The README now names them in one
    command and says why. pip has no such trap.

Documentation

  • Install leads with habit-hooks init; the four manual steps — install, add your
    language's plugin, enable it, install the detectors — are still there for anyone
    who wants them, because skipping one is the usual reason a first run reports
    nothing. Every detector is listed with the command that installs it, php
    included.

v1.0.0

Choose a tag to compare

@devill devill released this 03 Jul 14:12

First PyPI release of habit-hooks as a Python distribution.

Install: uv tool install habit-hooks / uvx habit-hooks / pip install habit-hooks — gives the core plus the language-agnostic generic plugin. Language plugins are opt-in: habit-hooks[python], [typescript], [php], or [all].

Published to PyPI: habit-hooks, habit-hooks-generic, habit-hooks-python, habit-hooks-typescript, habit-hooks-php (all 1.0.0).

0