claudemd-cli 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,63 @@ All notable changes to the `claudemd` plugin. This changelog tracks plugin artif
8
8
  - **Canonical spec version source**: `spec/CLAUDE.md` top-line title (`# AI-CODING-SPEC vX.Y.Z — Core`) + `spec/CLAUDE-changelog.md` top `##` entry.
9
9
  - **Plugin semver vs spec semver** are independent: plugin patch (0.2.0 → 0.2.1) may ship when spec is unchanged (this release); plugin minor (0.1.9 → 0.2.0) ships when spec minor updates (v0.2.0 shipped spec v6.10.0).
10
10
 
11
+ ## [0.58.0] - 2026-07-25
12
+
13
+ Fix batch from the 2026-07-25 deep four-dimension audit (`docs/comprehensive-audit-2026-07-25-v0.57.0-deep.md`, local-only per the `docs/` gitignore convention). Ships spec **v6.23.0**. Minor bump: the §8 deny surface widens across all three gates and two §7/§11 gates start firing on shapes they previously let through — user-visible default behavior change.
14
+
15
+ **What you must do**: nothing — the upgrade is drop-in. **What changes for you**: commands that previously slipped past the §8 gate now stop for confirmation (multi-target `rm -rf` with a variable, `exec`/`env -i`/`command -p`-wrapped commands, a redirection before the command word, `npm --yes exec`, `curl … |& sh`), and `git push` on its own line of a multi-line block now reaches the §7 and §11 gates it used to miss. **Opt-out** is unchanged and per-invocation: `[allow-rm-rf-var]` / `[allow-npx-unpinned]` / `[allow-curl-sh]` / `[skip-memory-check]` in the command, or `DISABLE_PRE_BASH_SAFETY_HOOK=1` / `DISABLE_SHIP_BASELINE_HOOK=1` / `DISABLE_MEMORY_READ_HOOK=1` to turn a hook off entirely. **Revert**: `/plugin install claudemd@claudemd` after pinning the marketplace entry to `v0.57.0`, or `git checkout v0.57.0 -- hooks/` in a local clone. One behavior contract also changed for scripted callers: `node scripts/doctor.js` now exits **3** when a health check fails (it always exited 0).
16
+
17
+ That audit broke a five-round streak of "production-grade, same conclusion" reports. The streak was probe homogeneity, not saturation: every prior round sampled the neighborhood of the existing corpus taxonomy, so a whole class of ordinary command spellings had never been tried.
18
+
19
+ ### §8 — one CRITICAL and four HIGH false negatives, all reproduced on the live hook
20
+
21
+ - **`rm` gate inspected only the FIRST positional target.** `rm_target` was bound once under a `[[ -z … ]]` guard, so `rm -rf ./build $VAR`, `rm -rf /tmp/a "$VAR"` and `: "${SAFE:?}" && rm -rf "$SAFE" "$EVIL"` all passed. This is the plain unvalidated-`$VAR` class the gate exists for, in an ordinary multi-target cleanup spelling. Every positional is now analyzed independently, so a validated target no longer vouches for its neighbours.
22
+ - **Wrapper taxonomy had three independent holes**, and because `s8_strip_wrappers` is correctly single-sourced across the rm / npx / curl-sh gates, each hole pierced all three at once: `exec` was absent entirely; `env` / `command` / `time` were filed ARGLESS so the first flag (`env -i`, `command -p`, `time -p`) ended the strip; and a redirection at command position (`>/tmp/log rm -rf $VAR`) was basename-canonicalized into a bare word that then read as the command. Adds `s8_wrap_optarg` for space-separated option arguments, which also retires the `sudo -u svc` and `timeout -s KILL 5` residuals.
23
+ - **npx family missed a global flag between tool and subcommand** — `npm --yes exec pkg`, `pnpm --silent dlx pkg` (both accepted by the real package managers).
24
+ - **curl-sh missed three delivery shapes**: `|&`, a sink behind a group opener (`| { bash; }`, `| (bash)`), and `bash < <(curl …)`.
25
+ - Evidence: corpus 330 → 374 rows, suite 469/469; differential scan against the pre-fix hook shows **0 verdict changes across all 330 pre-existing rows** — the widening is additive. Ten FP guards (`find … -exec rm {} +`, `npm run exec-tests`, `command -v rm`, `exec 3>&1`, mktemp-provenance cleanup, …) verified still allowed.
26
+
27
+ ### Gates that were not reading what they claimed to read
28
+
29
+ - **The heredoc-stripper fix had landed in one of three hand copies.** `memory-read-check` and `ship-baseline-check` never received pre-bash-safety's terminator lookahead, so `echo $((1<<n)) && git push` opened a phantom heredoc that swallowed the trigger — the §11 MEMORY.md gate and the §7 red-CI gate both stopped seeing it. Now a single `hook_strip_heredoc_bodies` in `hooks/lib/`. The shared version also keeps the opener line's tail, which the old copy discarded (`cat <<EOF && git push` runs that push).
30
+ - **A newline is a command separator, and both gates flattened it to a space.** Their trigger anchors require `^` or `[;&|]`, so a push on its own line of a multi-line block — the ordinary shape — reached neither gate. Shared `hook_flatten_cmd` now converts newlines to `;` and joins backslash continuations.
31
+ - **`ship-baseline` recorded an in-flight CI run as `pass`.** It read `.[0].conclusion`, which is null while a run executes, and null fell through to the pass arm. Under atomic ship that is the normal timing (pushing main starts CI; the tag push follows seconds later), so the gate reported a green baseline at exactly the moment it had no answer, and every count built on those rows was inflated. Now selects the newest *completed* run from a 5-run window; when nothing has completed it records `pending-no-baseline` instead of `pass`. Fixtures added for `in_progress`, `queued`, no-completed, and the two red conclusions (`action_required`, `startup_failure`) that the arm listed but nothing exercised.
32
+ - **`contract.test.sh` never opened `RULE-HITS-SCHEMA.md`** — the `SCHEMA` path was commented out with the note "was never read", and both invariants validated a hand-copied array whose failure message named the document. `mem-audit` had been emitting into the live log for months while absent from the schema, and the gate stayed green. It now parses the Events table and compares **(event, emitter) pairs**; matching bare event names meant a new emitter of an existing event checked out against the wrong row. Same treatment for `KNOWN_HOOK_SECTIONS` (covered 10 of ~16 sections) — now parsed from the taxonomy table.
33
+ - **`hard-rules-4` asserted the wrong invariant and locked the error in.** It required self-enforced entries to keep `rule_hits_section: null`, on a rationale ("until R-N8 lands") that expired two feature releases earlier — so five rules carried null while hooks emitted under their sections, and §13.1 demote review computed 0 hits for them *by construction*. Inverted: a rule whose section receives rows must declare it. `hard-rules-8` widens from deny-verbs to every emitted section. `hard-rules-audit.js` keys `hits` off the declared section rather than `enforcement`, so §11-session-exit (27 rows/30d) and §11-post-compaction (50) are visible for the first time.
34
+ - **`run-all.sh`'s repo-write guard checked only the commit count.** A suite that modified a tracked file or dropped an untracked artifact never commits, so the counter matched and the guard passed — on the atomic-ship path that artifact lands in the release commit. Now snapshots `git status --porcelain` too.
35
+ - **`npm run test:hooks` ran 1 of 23 suites and exited 0** (`bash tests/hooks/*.test.sh` treats the first glob match as the script and the rest as arguments).
36
+
37
+ ### Lifecycle and instruments
38
+
39
+ - **`install.js` accepted a plugin cache with no `hooks/hooks.json`**, registering zero hooks and reporting success; because the manifest version still matched, the SessionStart bootstrap read that as healthy and never retried. Now validated alongside the spec files, before anything user-owned is touched, with malformed-JSON and zero-hook cases covered.
40
+ - **`uninstall --purge` left three claudemd-owned files** in `~/.claude/logs/` (`claudemd.jsonl.1`, `.2`, `claudemd-bootstrap.log`), which also kept the empty-directory check from firing.
41
+ - **`doctor` always exited 0** — 4 of 42 failing checks still reported success, so anything gating on it was a no-op. Now exits **3** (not 1, which already means "argv rejected"). Its liveness kill-switch names come from `HOOK_REGISTRY` instead of a fourth parallel list, and a hook that exits at its kill-switch guard no longer counts as "ran clean".
42
+ - **`lesson-bypass-audit` joined a global log against a single project directory**, discarding 76% of the population as "missing transcript". Resolving each row against its own `project` field takes the measurable set from 57 to 165 suggestions; cite-recall moves 0.75 → 0.61 on the corrected denominator.
43
+ - **`sampling-audit --days` filtered file mtime only**, never turn timestamps, so a resumed session contributed every turn it ever held to a window that claimed otherwise — and `audit.js#selfCompliance` republished that number.
44
+ - **`version-cascade-check` passed a missing `spec/CLAUDE-extended.md`** with `ok: true` while the sibling unreadable-spec case failed. `spec-coherence-audit` had a second copy of the MEMORY.md link regex that took every match per line, which `memory-tags.js` documents as the bug it fixed.
45
+ - `npm-publish` now gates on a node 20 + 22 matrix with `fetch-depth: 0` before `publish` runs; previously the publish gate ran node 20 only (so the node:sqlite path was invisible to it) with a shallow checkout that made `upgrade-lifecycle` loud-skip, and had no relationship to `ci.yml`.
46
+ - Node test suites are scrubbed by `env-hygiene` when run via `npm run test:scripts`, not only under `run-all.sh`.
47
+
48
+ ### Spec v6.23.0
49
+
50
+ Detail in `spec/CLAUDE-changelog.md`. Headline: core no longer claims a Stop hook enforces §10's four-section order (it is advisory, opt-in, default-OFF, and cannot block); the §5.1 `aggressive` contradiction between core and §5.1-EXT is resolved in the stricter direction; §3's Order line ranks harness / MCP / skill instructions; the evidence ladder and cold-start are tagged L2+ where core points at them; §0.1's cap is stated in the bytes it has always been measured in and its headroom pointer names the file that holds the live Sizing line. `hard-rules.json` gains `§11-post-compaction` and declares the sections four self-enforced rules already emit under. Paired net-delete per §0.1: C7, C8, C12 — core 24714 → 24574B.
51
+
52
+ ## [0.57.0] - 2026-07-25
53
+
54
+ Closes the 2026-07-25 audit's remaining open items — five decided as "do", three explicitly closed as "won't do" (see `tasks/audit-2026-07-25-deferred.md`). Spec unchanged (**v6.22.0**). Minor bump: an opt-in hook is removed.
55
+
56
+ **What changes for users**: `mid-spine-yield-scan` is gone — if you had `MID_SPINE_YIELD_SCAN=1` set, that Stop advisory no longer fires and `DISABLE_MID_SPINE_YIELD_HOOK` / `/claudemd-toggle mid-spine-yield-scan` are no-ops (the §11 rule itself is unchanged and still self-enforced; only its observation mirror is retired). `banned-vocab` bypass rows change shape, and a same-day re-run of `/claudemd-sampling-audit` now refuses to overwrite its own report.
57
+
58
+ - **remove: the `mid-spine-yield-scan` Stop hook (16 → 15 hooks).** Zero events for its entire lifetime (v0.15.0 → now) — it shipped default-OFF per the behavior-layer convention and was never enabled anywhere, while still spawning a bash process on every Stop to exit at its opt-in guard, and its stderr text had drifted to quote the pre-v6.21.1 rule wording. By the project's own §13.1 criterion (hook-enforced, 0 hits in window) it was the cleanest demote candidate in the tree; syncing it to the v6.22.0 precondition would have been spend on something that has never run. `spec/hard-rules.json` is untouched: `§11-mid-spine-yield` is `enforcement: "self"` with `rule_hits_section: null`, so the §13 partition (7 hook / 14 self / 1 both / 1 external) is unaffected. Removed: the hook, its 12-case suite, the `hooks.json` registration, the registry entry (drops `DISABLE_MID_SPINE_YIELD_HOOK`), the doctor kill-switch row, the contract DOCUMENTED entry, and the count assertions in `full-lifecycle` / `hook-registry` / `install` / README (16 → 15 together). The `mid-spine-advisory` event is retired but historical rows stay valid — `session-end-check`'s L2+ heuristic still counts them.
59
+ - **feat(telemetry): `banned-vocab` bypass rows carry the term they suppressed.** The demote question is "which word does the operator keep overriding", and the data could not answer it: deny rows carried `{matched}`, bypass rows carried only `{token}`, and the token short-circuited before the scan ran. The scan now runs first and the bypass row is emitted at the hit with `{token, matched, path}`. **Measurement-semantics change**: a token on a command that would have passed anyway now records nothing (it was inflating the override rate with prophylactic tokens), so the 51.6% figure and any post-0.57.0 rate are not comparable. This is the instrumentation the §13.2 review needs before deciding anything about §10-V — the decision itself stays deferred.
60
+ - **fix(doctor): the demote label cited a rule the spec does not contain.** `rule-usage:<section>` reported "§0.1 demotion candidate" above a 50% bypass ratio, but §0.1 demotes on ZERO hits and a bypass threshold appears only as a §13.3 **promotion** gate (<10% to advance). The high-override signal is real; the action it licenses is a review, not a demotion. Relabeled to point at the §13.2 batch review and to say outright that no demote-by-bypass-rate rule exists — the mislabel had cost a full re-adjudication every run (`tasks/banned-vocab-demote-evaluation-2026-07-25.md` settled exactly this question for §10-V and the check kept re-asking it).
61
+ - **fix(hard-rules-audit): safety-class rules are exempt from the demote queue.** §8 rules are §5.1 Never-downgrade AND sparse by design — the attack surface they guard is rare, not absent — so listing `§8-curl-sh` as a 0-hit demote candidate recommended a forbidden action. They move to a new `safetyClassExempt` field, still visible (a safety gate that never fires may be broken, but that is an FN-matrix question, not a demotion one).
62
+ - **fix(sampling-audit): refuse to overwrite an existing report.** The output path is date-based, so a same-day re-run always collides — on 2026-07-25 a plain run replaced a committed, hand-annotated 216-line calibration record with a 16-line fresh scan (recovered via git). Now refuses unless `--force`; `--json` still prints without writing.
63
+ - **test: class-level repo-write guard in `run-all.sh`.** Records `git rev-list --count HEAD` before and after the suite and fails if it moved. `perf-baseline.sh` escaped for two months because only the files someone thought to check were checked; two lines now cover every existing and future suite. Verified RED against a deliberately-escaping fixture test.
64
+ - **docs**: the 0.55.0 entry's noop-commit count is corrected in place — 46 across two clusters (40 on 07-17, 6 on 05-10), not 48 all on one day; the May cluster dated to the night `perf-baseline.sh` first shipped and had gone unnoticed.
65
+
66
+ **Explicitly not done** (recorded in `tasks/audit-2026-07-25-deferred.md`): the §8-rm-rf-var scratch-cleanup friction — 0.56.0 changed the deny message to hand out the mktemp recipe, and measuring that intervention comes before stacking a second one; a byte cap on `MEMORY.md` — 78% of its 60 entries are durable `feedback_*` lessons, so a cap deletes lessons, and the recoverable space is in consolidating 9 closed-loop `project_*` audit entries; slash-command usage telemetry — an unused command's cost is 5,380 B of frontmatter, which compression addresses without building a measurement apparatus.
67
+
11
68
  ## [0.56.0] - 2026-07-25
12
69
 
13
70
  Fix batch from the 2026-07-25 four-dimension audit (`docs/comprehensive-audit-2026-07-25-v0.55.0.md`, local-only per the docs/ gitignore convention). Ships spec **v6.22.0** (minor). Minor bump: the §8 curl-sh and rm-rf-var deny surfaces both widen (user-visible default behavior change).
@@ -33,7 +90,7 @@ Ships spec **v6.21.2** (patch) plus two hermeticity/observability fixes surfaced
33
90
  **What changes for users**: SessionStart now banners when Claude Code is loading hooks from an older build than the marketplace cache holds — the exact drift that let two releases (0.53.0, 0.54.0) run unnoticed on a stale 0.52.0 install; `scripts/perf-baseline.sh` can no longer commit into your repo or write live telemetry.
34
91
 
35
92
  - **feat(session-start): stale-registration banner (`stale_cache_check`).** `upstream_check` compares the cache max against the REMOTE tag, so after a release + local marketplace update (cache == remote) it stays silent even though the registered root is older — reproduced 2026-07-25: hooks running v0.52.0 while cache and remote both held v0.54.0, zero banners for two releases; only `doctor`'s on-demand `plugin cache:staleness` check saw it. The new check compares the running root against the local cache max — no network, no 24h sentinel, repeats every SessionStart until `/claudemd-refresh` re-registers. Skips the redundant remote probe when it fires. Telemetry reuses the registered `stale-root` event with `{hook_version, cache_max_version}` extra (no new event name). Shared `semver_cache_max()` helper replaces the inline glob in `upstream_check` (one source for the cache scan). Tests: `session-start.test.sh` cases 23-24 — case 23 RED pre-fix (hook emitted nothing on a newer cache), 24/24 post-fix.
36
- - **fix(perf-baseline): probes run in a throwaway repo + telemetry off.** The `git commit --allow-empty -m 'noop'` probe executed in the CALLER's cwd — 48 stray `noop` commits landed on this repo's main on 2026-07-17 (all stamped 15:14:04-05) and were pushed with the next release. All probe commands now execute inside a `mktemp -d` sandbox repo (trap-cleaned, spec §8.V3), and the script exports `DISABLE_RULE_HITS_LOG=1` so its synthetic hook invocations stop polluting live telemetry (`feedback_manual_hook_probe_pollutes_telemetry`). New regression gate `tests/integration/perf-baseline-hermetic.test.sh`: RED pre-fix (caller repo commits 1 → 3 at `--runs 1`), 3/3 post-fix.
93
+ - **fix(perf-baseline): probes run in a throwaway repo + telemetry off.** The `git commit --allow-empty -m 'noop'` probe executed in the CALLER's cwd — 48 stray `noop` commits landed on this repo's main on 2026-07-17 (all stamped 15:14:04-05) and were pushed with the next release. *(Correction, 0.57.0: the real count is 46 across **two** clusters — 40 on 2026-07-17 15:14:04-05 and 6 on 2026-05-10 02:58:04-05, the night `perf-baseline.sh` first shipped in v0.9.7. The May cluster went unnoticed at the time and was folded into the July narrative above. `git log --grep='^noop$'` is the check.)* All probe commands now execute inside a `mktemp -d` sandbox repo (trap-cleaned, spec §8.V3), and the script exports `DISABLE_RULE_HITS_LOG=1` so its synthetic hook invocations stop polluting live telemetry (`feedback_manual_hook_probe_pollutes_telemetry`). New regression gate `tests/integration/perf-baseline-hermetic.test.sh`: RED pre-fix (caller repo commits 1 → 3 at `--runs 1`), 3/3 post-fix.
37
94
  - **change(spec v6.21.2): §EXT §10-V compressed to OK-shapes + pointers** — rationale and demote-evaluation record in `spec/CLAUDE-changelog.md`. `banned-vocab-canonical.json` updated in step (pattern rows kept as in_spec=false, ten spec-only rows retired); `hooks/banned-vocab.patterns` untouched — mechanical enforcement byte-identical.
38
95
 
39
96
  ## [0.54.0] - 2026-07-24
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # claudemd
2
2
 
3
- > A **personal AI-coding discipline harness**: one developer's opinionated **AI-CODING-SPEC v6.22**, encoded as Claude Code shell hooks and shipped with the plugin. Built and dogfooded on my own repos — fork and adapt, don't adopt wholesale.
3
+ > A **personal AI-coding discipline harness**: one developer's opinionated **AI-CODING-SPEC v6.23**, encoded as Claude Code shell hooks and shipped with the plugin. Built and dogfooded on my own repos — fork and adapt, don't adopt wholesale.
4
4
 
5
5
  [![CI](https://github.com/sdsrss/claudemd/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/sdsrss/claudemd/actions/workflows/ci.yml)
6
6
  [![npm](https://img.shields.io/npm/v/claudemd-cli.svg)](https://www.npmjs.com/package/claudemd-cli)
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
8
 
9
- claudemd plugs into the Claude Code hook system to **block commits, pushes, and bash commands** that violate AI-CODING-SPEC v6.22 — banned vocabulary in commit messages, `rm -rf $VAR` without variable validation, ship-on-red-CI, unread `MEMORY.md` entries during release flows, and more. The spec itself (`CLAUDE.md` + `CLAUDE-extended.md` + `CLAUDE-changelog.md` + `OPERATOR.md`) ships with the plugin and installs into `~/.claude/`, so the rules Claude Code reads at session start match the rules the hooks enforce. (`OPERATOR.md` is the human-only spec-maintenance handbook — Agent-loaded files are the CLAUDE trio.)
9
+ claudemd plugs into the Claude Code hook system to **block commits, pushes, and bash commands** that violate AI-CODING-SPEC v6.23 — banned vocabulary in commit messages, `rm -rf $VAR` without variable validation, ship-on-red-CI, unread `MEMORY.md` entries during release flows, and more. The spec itself (`CLAUDE.md` + `CLAUDE-extended.md` + `CLAUDE-changelog.md` + `OPERATOR.md`) ships with the plugin and installs into `~/.claude/`, so the rules Claude Code reads at session start match the rules the hooks enforce. (`OPERATOR.md` is the human-only spec-maintenance handbook — Agent-loaded files are the CLAUDE trio.)
10
10
 
11
11
  A standalone CLI (`npx claudemd-cli`) reuses the same `banned-vocab.patterns` source for git pre-commit hooks, GitHub Actions, and other agents that don't run inside Claude Code.
12
12
 
@@ -57,10 +57,10 @@ Verify in one command (Linux): `node --version && jq --version && gh --version &
57
57
 
58
58
  | Layer | Contents |
59
59
  |---|---|
60
- | 16 shell hooks | `banned-vocab-check` · `pre-bash-safety-check` · `ship-baseline-check` · `residue-audit` · `memory-read-check` · `memory-prompt-hint` · `mid-spine-yield-scan` · `sandbox-disposal-check` · `session-start-check` · `session-extended-read` · `session-summary` · `session-end-check` · `transcript-vocab-scan` · `transcript-structure-scan` · `version-sync` · `mem-audit` |
60
+ | 15 shell hooks | `banned-vocab-check` · `pre-bash-safety-check` · `ship-baseline-check` · `residue-audit` · `memory-read-check` · `memory-prompt-hint` · `sandbox-disposal-check` · `session-start-check` · `session-extended-read` · `session-summary` · `session-end-check` · `transcript-vocab-scan` · `transcript-structure-scan` · `version-sync` · `mem-audit` |
61
61
  | 16 slash commands | `/claudemd-install` · `/claudemd-status` · `/claudemd-update` · `/claudemd-refresh` · `/claudemd-audit` · `/claudemd-toggle` · `/claudemd-doctor` · `/claudemd-analyze` · `/claudemd-uninstall` · `/claudemd-rules` · `/claudemd-clean-residue` · `/claudemd-sparkline` · `/claudemd-sampling-audit` · `/claudemd-bypass-audit` · `/claudemd-design-adopt` · `/claudemd-statusline` |
62
62
  | 1 standalone CLI | `claudemd-cli lint` · `claudemd-cli audit` ([npm: `claudemd-cli`](https://www.npmjs.com/package/claudemd-cli)) |
63
- | Spec v6.22 | `~/.claude/CLAUDE.md` · `CLAUDE-extended.md` · `CLAUDE-changelog.md` · `OPERATOR.md` (backup-before-overwrite) |
63
+ | Spec v6.23 | `~/.claude/CLAUDE.md` · `CLAUDE-extended.md` · `CLAUDE-changelog.md` · `OPERATOR.md` (backup-before-overwrite) |
64
64
  | StatusLine (opt-out) | PS1-style line — `user@host:dir (branch) Model [ctx:N% · 5h:N% · 7d:N%]` (`dir` = cwd basename; context / 5-hour quota / weekly quota, all **used %**, read from Claude Code's `rate_limits` payload; quota segments auto-hide when the data is absent, or force-hide with `DISABLE_STATUSLINE_QUOTA=1`) — wired into `~/.claude/settings.json` on install **only when the slot is empty**; an existing statusline is left untouched. Skip entirely with `CLAUDEMD_NO_STATUSLINE=1`. Manage via `/claudemd-statusline`. |
65
65
 
66
66
  Install backs up a hand-written `~/.claude/CLAUDE.md` (any file without the `# AI-CODING-SPEC` H1) to `~/.claude/backup-<ISO>/` before overwriting (last 5 kept automatically). An already-installed claudemd spec is overwritten **without** a backup — deliberate (v0.23.11): the sole backup is always your own content, so `restore` can never return a stale spec instead. Uninstall offers `keep / restore / delete`; `delete` requires an extra confirmation.
@@ -183,7 +183,6 @@ export DISABLE_TRANSCRIPT_STRUCTURE_SCAN_HOOK=1 # v0.9.10+ — Stop §10 four-s
183
183
  export DISABLE_MEM_AUDIT_HOOK=1 # v0.9.4+ — Stop Why:-less citation advisory
184
184
  export DISABLE_SESSION_END_CHECK_HOOK=1 # v0.9.27+ — SessionEnd §11-session-exit mid-SPINE check
185
185
  export DISABLE_SESSION_EXTENDED_READ_HOOK=1 # v0.10.1+ — PreToolUse:Read §13.1-extended-read denominator signal
186
- export DISABLE_MID_SPINE_YIELD_HOOK=1 # v0.15.0+ — Stop §11-mid-spine-yield advisory (opt-in via MID_SPINE_YIELD_SCAN=1)
187
186
  ```
188
187
 
189
188
  **2a. Per-sub-feature** (v0.4.0+). Sub-flags inside an enabled hook, named without the `_HOOK` suffix:
@@ -348,12 +347,12 @@ claudemd/
348
347
  ├── .claude-plugin/
349
348
  │ ├── plugin.json # minimal manifest (name, version, author, license, keywords)
350
349
  │ └── marketplace.json # marketplace catalog entry
351
- ├── hooks/ # 16 shell hooks + hooks/lib/ (hook-common, rule-hits, platform)
350
+ ├── hooks/ # 15 shell hooks + hooks/lib/ (hook-common, rule-hits, platform)
352
351
  │ └── hooks.json # authoritative hook registration (v0.1.5+); CC expands ${CLAUDE_PLUGIN_ROOT} here
353
352
  ├── commands/ # 16 slash-command markdown files
354
353
  ├── bin/ # standalone CLI entrypoint (claudemd-lint.js → `npx claudemd-cli` on npmjs.org)
355
354
  ├── scripts/ # 18 Node.js scripts + scripts/lib/ (single-source registry, lint, etc.)
356
- ├── spec/ # shipped v6.22.0 CLAUDE*.md trio + OPERATOR.md + hard-rules.json manifest
355
+ ├── spec/ # shipped v6.23.0 CLAUDE*.md trio + OPERATOR.md + hard-rules.json manifest
357
356
  ├── tests/ # hook shell tests + Node.js tests + integration + fixtures
358
357
  ├── docs/ # ADDING-NEW-HOOK.md + RULE-HITS-SCHEMA.md + superpowers/
359
358
  └── .github/workflows/ # ci.yml (ubuntu+macOS × node 20) + npm-publish.yml (tag-triggered)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemd-cli",
3
- "version": "0.56.0",
3
+ "version": "0.58.0",
4
4
  "description": "Standalone CLI for §10-V banned-vocab + transcript scanning. Companion to the claudemd Claude Code plugin (github.com/sdsrss/claudemd) for use in git pre-commit hooks, GitHub Actions, and other agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,8 +23,8 @@
23
23
  ],
24
24
  "scripts": {
25
25
  "test": "bash tests/run-all.sh",
26
- "test:scripts": "node --test tests/scripts/*.test.js",
27
- "test:hooks": "bash tests/hooks/*.test.sh",
26
+ "test:scripts": "bash -c 'source tests/lib/env-hygiene.sh && claudemd_reset_test_env && node --test tests/scripts/*.test.js'",
27
+ "test:hooks": "for t in tests/hooks/*.test.sh; do bash \"$t\" || exit 1; done",
28
28
  "lint:argv": "node scripts/lint-argv.js",
29
29
  "version-check": "node scripts/version-cascade-check.js"
30
30
  },