claudemd-cli 0.37.1 → 0.39.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,26 @@ 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.39.0] - 2026-07-12
12
+
13
+ **Minor — §8 Bash-gate false-negative closure: command-name canonicalization + shell-lexer normalization (roadmap SEC-1).** Closes 4 novel §8 bypasses surfaced by the 2026-07-12 production-readiness audit (`docs/production-readiness-audit-2026-07-12.md`, all reproduced live before the fix). Spec unchanged (stays v6.17.0). **User-visible enforcement change**: commands previously ALLOWED are now denied — see below.
14
+
15
+ - **Fix** (`hooks/pre-bash-safety-check.sh`): the rm-detector's segment gate matched only the bare literal token `rm`, and the sanitizer never normalized `${IFS}` or backslash-newline, so four natural-looking evasions slipped the gate. All now denied:
16
+ - **F1 command-name canonicalization** — the rm command word is canonicalized to its basename with one leading backslash stripped, so `/bin/rm -rf $VAR`, `./rm`, and the alias-defeating `\rm -rf $VAR` are recognized as rm (matching what the shell execs, not the source spelling). `busybox rm` (multiplexer) added to the wrapper-strip set. Exact `== rm` after canonicalization keeps `charm`/`norm`/`perm` off.
17
+ - **F2 `${IFS}`/`$IFS` word-split** — folded to a space before tokenizing, so `rm${IFS}-rf${IFS}$HOME` and `npx${IFS}pkg` read as `rm -rf $HOME` / `npx pkg`. Bare `$IFS` folds only when not followed by an identifier char (`$IFSFOO` untouched).
18
+ - **F3 backslash-newline continuation** — `rm -r\`+newline+`f $X` rejoins before tokenizing (portable bash-3.2 param-expansion, not BSD-unsafe sed `\n`).
19
+ - **F4 `source <(curl)` / `. <(curl)`** — `source` and `.` added to the curl-process-substitution interpreter set; they exec fetched content in the current shell like `bash <(curl x)`.
20
+ Folding/canonicalization can only EXPOSE tokens the shell itself sees at exec time, never hide one, so these strictly close false-negatives on the deny-on-match detectors. **Positioning unchanged**: §8 remains a guardrail (steers the agent off its own mistakes + makes rule-adherence observable), NOT an anti-injection security boundary — any `DISABLE_*` env var or `[allow-*]` token still bypasses by design. Documented residuals (`xargs rm`, option-with-arg wrappers like `sudo -u svc rm`, non-shell interpreter sinks) remain out of scope (diminishing returns; don't change the guardrail framing).
21
+ - **Tests** (`tests/fixtures/bash-safety/corpus.tsv`, +16 rows → suite 211 → 222): 11 adversarial deny cases (F1–F4) + 5 FP controls (`/bin/rm` literal path, `charm` basename exact-match, `${IFS}` in echo, `source`/`.` on local files) — the red-team evasion corpus the roadmap called for, driven by `pre-bash-safety.test.sh` inside the CI-gated run-all. `npm test` all suites pass; shellcheck clean; bash-3.2 portability gate clean.
22
+
23
+ ## [0.38.0] - 2026-07-12
24
+
25
+ **Minor — statusline path segment renders cwd basename only.** Spec unchanged (stays v6.17.0).
26
+
27
+ - **User-visible change** (`scripts/statusline.sh`): the blue path segment now shows the cwd basename — `sds@nb:claudemd (main) …` — instead of the full path, which crowded out the branch/model/meter segments on deep project paths. `cwd="/"` (or any cwd whose basename is empty, e.g. trailing slash) falls back to the full cwd; branch detection is untouched (still runs against the full path). Revert: the rendering script is a stable copy at `~/.claude/claudemd-statusline.sh` — restore it from an older plugin cache dir, or point the statusLine slot elsewhere via `/claudemd-statusline remove`.
28
+ - **Docs** (`README.md`): StatusLine row format string updated (`user@host:/path` → `user@host:dir`, `dir` = cwd basename).
29
+ - **Tests** (`tests/scripts/statusline.test.js`): 23 → 24 — full-payload assertion now expects the basename AND asserts the full path is absent; new `cwd:"/"` fallback case.
30
+
11
31
  ## [0.37.1] - 2026-07-11
12
32
 
13
33
  **Patch — QA tech-debt closure: per-suite env hygiene + README drift guards + sampling-audit zero-data skip.** Closes the three follow-ups filed by the 2026-07-11 QA loop. Spec unchanged (stays v6.17.0).
package/README.md CHANGED
@@ -61,7 +61,7 @@ Verify in one command (Linux): `node --version && jq --version && gh --version &
61
61
  | 15 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` · `/claudemd-statusline` |
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.17 | `~/.claude/CLAUDE.md` · `CLAUDE-extended.md` · `CLAUDE-changelog.md` · `OPERATOR.md` (backup-before-overwrite) |
64
- | StatusLine (opt-out) | PS1-style line — `user@host:/path (branch) Model [ctx:N% · 5h:N% · 7d:N%]` (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`. |
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.
67
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemd-cli",
3
- "version": "0.37.1",
3
+ "version": "0.39.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": {