claudemd-cli 0.23.22 → 0.24.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,45 @@ 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.24.0] - 2026-07-05
12
+
13
+ **Minor — design-adopt: a new `/claudemd-design-adopt` command generates a thin, fact-based `DESIGN.md` from a UI project's real design-token sources and wires it into project CLAUDE.md, so agents use the project's tokens instead of inventing colors/spacing.** Spec content unchanged (stays v6.14.1). Origin: comparative analysis of a community AGENTS.md generator (whose template-stuffing approach is this feature's explicit anti-goal) + a manual prototype on a real Vue/Element-Plus product repo; spec at `tasks/specs/design-adopt.md`.
14
+
15
+ **What changes for you**: a new opt-in slash command, nothing else. There is **no SessionStart hook and nothing auto-fires** — you invoke `/claudemd-design-adopt` when you want it (Claude may also suggest it when you're doing UI work in a token-bearing project). It detects the project's design tokens, drafts a `DESIGN.md` that points at the real source-of-truth files (never duplicates or invents values), wires a sentinel block into project CLAUDE.md, and **always shows the diff and asks before writing**.
16
+
17
+ Built in three passes and two adversarial max-effort reviews. The first review (40 findings) drove a correctness sweep; the second (adversarial re-review, 40 findings) showed the severe issues concentrated in an earlier **SessionStart auto-hint** design — cache/pending-file collisions, statefile races, false-positive nagging, and residue. Rather than keep hardening a heuristic that fires silently, the auto-hint was **cut**: the detector is now stateless and command-only, so the human's diff+consent gate is the safety net and the entire hint-path failure class is designed out. Findings + resolutions: `tasks/design-adopt-review-findings-2026-07-05.md`.
18
+
19
+ ### Added — `scripts/design-detect.js` (deterministic, stateless detector, zero-LLM)
20
+
21
+ - Classifies a repo `no-ui | ui-no-tokens | adoptable | unwired | configured` from filesystem facts, writing **nothing** to disk (no cache, no state). package.json deps drive the UI signal: direct **and** meta-frameworks (Nuxt/Astro/Remix/Gatsby/SvelteKit, whose base framework is only transitive), component-lib and atomic-CSS lists; a subproject fallback covers declared workspaces (`packages/*` + `apps/*`) and the workspace-less fullstack split (empty root + a strong SPA-root subdir — `frontend|web|client` only, so an incidental `site/`/`app/` in a backend repo does not false-positive). Bounded walk (depth ≤4, ≤400 dirs, ≤60 CSS content-scans) anchored at the **UI subproject** when one is identified — so a monorepo's non-UI sibling package can't donate its token files to the UI package, and deep `packages/<pkg>/src/assets/styles/` tokens stay reachable. Token counting parses `:root` **and** Tailwind-v4 `@theme` blocks with a brace-depth scan that is robust to nested SCSS/Less interpolation (`#{map-get($m, #{$k})}`), unterminated `/* comments`, nested selectors, and digit/underscore-first custom-property names (`--2xl`, `--_gap`); component-scoped props with a `:root` mention only in a comment do not count. Wiring check reads the **full** CLAUDE.md (root + subproject) and requires a `DESIGN.md` reference or the adopt sentinel — a bare token-basename mention (e.g. `app.css`) is not accepted. FIFO-safe reads (isFile-guarded); `pathToFileURL` main guard (runs from paths with spaces / non-ASCII). Fail-open: exit 0 on every verdict, argv errors exit 2 (`parseStrict`).
22
+
23
+ ### Added — `/claudemd-design-adopt` (agent contract)
24
+
25
+ - `commands/claudemd-design-adopt.md`: detect → read real token sources → draft DESIGN.md under a facts-only contract (thin pointer to the source-of-truth files; hard rules from an evidence-gated menu — semantic vars / dark mode / spacing base / mixin reuse / mono font / contrast commitments — no rule without evidence, no invented values, no generic boilerplate) → wire an idempotent `<!-- claudemd-design:begin v1 -->` sentinel block into project CLAUDE.md (for a monorepo, the subproject CLAUDE.md the detector's wiring check also reads) → mandatory diff + consent before writing (even under `AUTONOMY_LEVEL: aggressive`) → re-run detector and cite `verdict: configured` as completion evidence. `check` verifies DESIGN.md pointers resolve; `remove` unwires the block (never deletes DESIGN.md). Domain color semantics (e.g. finance red-up/green-down) are marked TODO for the human unless a source comment states them.
26
+
27
+ ### Tests
28
+
29
+ - `tests/scripts/design-detect.test.js` (19 cases): verdict matrix over 16 fixtures; FP traps (node_modules token file, <8-prop `:root`, `:root`-in-comment + component-scoped props, unterminated-comment block, backend-with-`site/`, bare-basename wiring mention, monorepo cross-package token misattribution); meta-framework Nuxt, Tailwind v4 `@theme`, deep-monorepo + fullstack subproject walk; nested-SCSS-interpolation and digit/underscore-first custom-prop locks; byte-exact real-repo `variables.scss` lock; >64KB CLAUDE.md wiring; FIFO non-hang; stable token-set enumeration; path-with-space main guard; CLI contract. No SessionStart hook changes (that integration was cut). Full suite 523 → 542 pass. Real-repo verification: the motivating fullstack Vue repo detects `configured` (2 token sources), this plugin repo `no-ui`.
30
+
31
+ ## [0.23.23] - 2026-07-03
32
+
33
+ **Patch — a self-audit found a doctor↔audit telemetry-parity gap, then an adversarial §8 false-negative sweep (hardened by a code review) closed four "execute scripts of unknown origin" bypass classes in the pre-bash-safety hook.** Spec content unchanged (stays v6.14.1).
34
+
35
+ ### Fixed — `doctor`: rule-usage counted test-session/probe rows that `audit.js` filters
36
+
37
+ - `scripts/doctor.js`: the `rule-usage` health check derived its deny/bypass counts (and the §0.1 demote verdict downstream of them) from the raw `readHits` output, while the sibling `audit.js` strips manual-probe / sentinel-session rows via `excludeTestSessions`. On the same 30-day window doctor over-reported — `§7-ship-baseline` deny 17 vs audit's 9, `§8-rm-rf-var` 121 vs 101. Both rule-usage consumers (the `groupBySection` count + the demote-candidate token-breakdown loop) now filter identically; the `fail-open` check stays on raw hits by design (its rows are `session_id:null`). Doctor now matches audit.js across every section. Tests: `doctor.test.js` +2 (sentinel-exclusion count + token-breakdown).
38
+
39
+ ### Fixed — §8: four "execute scripts of unknown origin" false negatives in `pre-bash-safety`
40
+
41
+ A 2026-07-03 adversarial audit (feed dangerous commands to the detector, observe allow/deny) plus a `superpowers` code review found the hook enforced only the literal forms of §8's unknown-origin / NPX rules. Four classes now covered — each adds denials only, verified against a false-positive control set:
42
+
43
+ - **Fetch-execute runner family**: the NPX gate matched only literal `npx`; `pnpm dlx` / `yarn dlx` / `bunx` / `npm exec` of an unpinned unknown package bypassed it (identical fetch-execute — `npx` is a shortcut for `npm exec`). `NPX_REGEX` extended to the family, reusing the existing pinned/local/lockfile resolution (already reads pnpm-lock.yaml / yarn.lock). `npm install` / `pnpm install` / `yarn add` stay excluded (the regex requires the `exec` / `dlx` subcommand).
44
+ - **`curl … | sh`** (new Pattern 3): no detector existed for piping or `<()`-substituting a network fetch into a shell. Fires when `curl`/`wget` in command position feeds `sh`/`bash`/`zsh`/`dash`/`ksh`/`ash` (optionally via `sudo`), matched per pipeline segment on the sanitized command. Local/literal sources (`cat x.sh | sh`), non-shell sinks (`| jq`), download-only (`curl -o`), and prose-in-quotes stay allowed. New `§8-curl-sh` telemetry section + `[allow-curl-sh]` bypass token.
45
+ - **rm behind wrappers**: `sudo` / `doas` / `timeout N` / `nice -nN` / `stdbuf` / `ionice` / `chrt` before `rm -rf $VAR` bypassed the segment-start `rm` check. The wrapper-strip loop now handles them (arg-less + flag-bearing, consuming a wrapper's options and numeric/duration args, stopping at the first command word). Stripping only removes prefixes, so a non-rm command or a safe rm (literal path / `$HOME` subpath) never false-denies.
46
+ - **Code review follow-up** (folded in): `sudo -E/-i/-H/-n rm` (sudo is flag-bearing, not arg-less), `npm exec`, and brace-group `{ curl … | sh; }` were caught adversarially after the initial fixes. Documented residuals: option-with-argument wrapper forms (`sudo -u svc rm`, `timeout -s KILL 5 rm`), `eval "$(curl)"`, `find -delete`.
47
+
48
+ Hook tests +50 (206 in `pre-bash-safety.test.sh`); full suite 523 pass. `docs/RULE-HITS-SCHEMA.md` synced (new `§8-curl-sh` section + `allow-curl-sh` token). Findings + deferred residuals recorded in `tasks/s8-false-negative-audit-2026-07-03.md` and `tasks/project-audit-findings-2026-07-03.md`.
49
+
11
50
  ## [0.23.22] - 2026-07-02
12
51
 
13
52
  **Patch — follow-up to v0.23.21 (code review): sync the two remaining dedup-key reference docs + harden the `extra` key against future key-order variance.** Spec content unchanged (stays v6.14.1).
package/README.md CHANGED
@@ -58,7 +58,7 @@ Verify in one command (Linux): `node --version && jq --version && gh --version &
58
58
  | Layer | Contents |
59
59
  |---|---|
60
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` |
61
- | 13 slash commands | `/claudemd-install` · `/claudemd-status` · `/claudemd-update` · `/claudemd-audit` · `/claudemd-toggle` · `/claudemd-doctor` · `/claudemd-analyze` · `/claudemd-uninstall` · `/claudemd-rules` · `/claudemd-clean-residue` · `/claudemd-sparkline` · `/claudemd-sampling-audit` · `/claudemd-bypass-audit` |
61
+ | 14 slash commands | `/claudemd-install` · `/claudemd-status` · `/claudemd-update` · `/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` |
62
62
  | 1 standalone CLI | `claudemd-cli lint` · `claudemd-cli audit` ([npm: `claudemd-cli`](https://www.npmjs.com/package/claudemd-cli)) |
63
63
  | Spec v6.14 | `~/.claude/CLAUDE.md` · `CLAUDE-extended.md` · `CLAUDE-changelog.md` · `OPERATOR.md` (backup-before-overwrite) |
64
64
 
@@ -113,6 +113,7 @@ Per-hook timeout (3-5s in `hooks.json`); timeout = treated as exit 0 (pass) per
113
113
  | `/claudemd-rules [N]` | v0.8.0+ — audit `spec/hard-rules.json` manifest over last N days (default 30 — lowered from 90d in v0.13.1 after the 90d gate was structurally unreachable under typical log retention). Surfaces `demoteCandidates` (hook-enforced rules with 0 hits) and `staleReviews` (rules whose `last_demote_review` is null/old). |
114
114
  | `/claudemd-sparkline [--days=A,B,C]` | v0.8.4+ R-N9 — per-`spec_section` cumulative counts of signal events across 3 windows (default 30/60/90d). Trend arrow compares per-period rate; `(newly active)` / `(silenced)` annotations flag activation/deactivation transitions. Markdown block suitable for CHANGELOG header pre-release. |
115
115
  | `/claudemd-clean-residue [--apply]` | Dry-run-by-default cleanup of stale `claudemd-sync-*` sentinels and historical `claudemd-(mockgh\|work).*` test sandboxes. |
116
+ | `/claudemd-design-adopt [check\|remove]` | v0.24.0 — for a UI project, generate a thin, fact-based `DESIGN.md` from its real design-token sources (deterministic detector `scripts/design-detect.js`; evidence-gated rules menu; never invents values) and wire a sentinel block into project CLAUDE.md. `check` verifies pointers resolve; `remove` unwires. Always shows the diff and asks before writing. Manual/opt-in — no SessionStart hook, nothing auto-fires. |
116
117
  | `/claudemd-uninstall` | Pre-uninstall cleanup: clears manifest + state + log + legacy `settings.json` hook entries. Run BEFORE `/plugin uninstall claudemd@claudemd` (see [Uninstall](#uninstall)). |
117
118
 
118
119
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemd-cli",
3
- "version": "0.23.22",
3
+ "version": "0.24.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": {