Skip to content

neckbeard 1.14.0

Choose a tag to compare

@jerrydboonstra jerrydboonstra released this 15 Oct 02:18
· 11 commits to main since this release
  • CONTRIBUTING.md has a "Merging several branches" section, and the layout follows it. CHANGELOG.md merges by union, make check runs every tools/test_*.py instead of naming each, and .PHONY lists one target per line. A trial merge of six open branches conflicted only in those two files.
  • The plugin has no top-level bin/ at all. 1.12.9 moved excerpts out but kept bin/neckbeard, so every session still got a neckbeard command on its PATH, and claude.ai Chat and Cowork still refused the whole plugin, because they reject any plugin with a top-level bin/. bin/neckbeard, a wrapper that only ran skills/neckbeard/inventory.py, is now tools/neckbeard (the skill itself has called inventory.py from its own folder since 1.12.2). Removed: a bare neckbeard command is no longer on your PATH. From a clone, run tools/neckbeard <target>. make check now fails if a top-level bin/ comes back.
  • The dossier carries the text the judge needs, so two runs judge the same thing. Run twice on Cat Herder, neckbeard named 5 conflicts, then 2, because the dossier held only each SKILL.md and the judge opened the role and section files by hand, differently each time. Each skill now lists its other Markdown files, with their text, as companion_files (capped at 40 per skill; a file past the cap or outside the target is named, never read). A plugin whose plugin.json gives hooks as a list of files (Cat Herder's does) is now read and merged instead of reported unknown; a missing or outside entry still makes the answer unknown. Each skill also carries user_invocable, read from its frontmatter, so no run has to work that out by hand. SKILL.md is unchanged. Five self-check cases; three fail against 1.13.0, and the missing-file and outside-target cases guard the new list path.
  • The dossier flags frontmatter that strict YAML cannot read. Claude Code loads a skill whose description has an unquoted : without complaint, so the author never sees the fault, while GitHub shows "Error in user YAML" and stricter agents may drop the skill. Each skill, command and agent that has such a fault now carries frontmatter_errors (a line and a message), and only those do. The check is a small stdlib one, not a full YAML parser: it covers an unquoted : (in a plain value, a continued one or a nested one), a value starting with @, a backtick, *, %, !, - or ? , an apostrophe inside a single-quoted value, text after a closing quote or a block header, an unclosed quote or bracket, a tab in indentation and a frontmatter fence that never closes. A trailing # comment is not part of the value. A # debt: comment in the code names what it misses. On 70 real skill, agent and command files it agreed with PyYAML on all 70, and on a list of hand-written values it disagreed once, which was fixed (an anchor is valid). The report does not mention it yet: that needs a change to SKILL.md and a sealed-fixture version, which is the owner's call. Twenty self-check cases.
  • make mutate tests the claim that every self-check case has been seen to fail. tools/mutate-selfcheck makes 452 copies of inventory.py with one condition deleted each, runs the self-check against every copy and lists the cases that no copy made fail; --targeted breaks by hand the behaviours generic mutation cannot reach (a merge order, a regex). The first run found 8 of 59 cases that never failed. One was weak: "local true overrides project false" passed with every later settings layer ignored, because its final value equalled the global one (its fixture now uses a key the global layer never sets). The other seven, "no settings anywhere" among them, fail under the hand-made sabotages. It is heavy (10 jobs, several minutes), so it is not part of make check.
  • tools/vet-stability shows how far repeated reports agree. Give it two or more reports for one unchanged target and it prints each verdict and count line, then every conflict with how many reports found it ("found in 4 of 5"), and exits 1 when the verdicts or the conflict lists differ. A conflict is matched by the rule it collides with (the rule file and number in "held in", punctuation ignored) and, when the "it says" cell names one, the file it comes from, never by wording, since the judge words one finding differently each run. Two findings from one file against one rule count as one, and the output says so. It needs only python3, so it costs no model run. tools/vet-repeat TARGET OUTDIR N runs tools/vet N times and then this (each run is a real judge session). This is the measuring half of verdict-stability; the measured runs and the judge changes follow.
  • A single rules file reads only itself. Pointed at one file, neckbeard used to follow its @path lines and read the files they named, so a downloaded rules.md could pull its neighbours into the dossier. Now only a file named CLAUDE.md follows imports, as Claude Code does, and still only inside its own folder; any other file lists the names it gives under imports_not_followed and reads none of them. A binary or unreadable file is refused with an error instead of producing an empty dossier.