{"id":49214207,"url":"https://github.com/sdsrss/claudemd","last_synced_at":"2026-06-09T19:00:31.270Z","repository":{"id":352795356,"uuid":"1216371597","full_name":"sdsrss/claudemd","owner":"sdsrss","description":"Claude Code plugin: AI-CODING-SPEC v6.11 enforcement via shell hooks + spec distribution into ~/.claude/. Blocks banned vocab in commit messages, unsafe rm -rf $VAR, push-on-red-CI, unread MEMORY.md before ship. Ships a standalone npx CLI for git pre-commit + GitHub Actions.","archived":false,"fork":false,"pushed_at":"2026-06-02T19:11:33.000Z","size":1441,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-02T19:12:21.660Z","etag":null,"topics":["banned-vocab","claude-code","claude-code-plugin","code-quality","commit-lint","hooks","npm-cli","spec-enforcement"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/claudemd-cli","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sdsrss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-20T20:52:07.000Z","updated_at":"2026-06-02T19:09:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sdsrss/claudemd","commit_stats":null,"previous_names":["sdsrss/claudemd"],"tags_count":114,"template":false,"template_full_name":null,"purl":"pkg:github/sdsrss/claudemd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsrss%2Fclaudemd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsrss%2Fclaudemd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsrss%2Fclaudemd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsrss%2Fclaudemd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sdsrss","download_url":"https://codeload.github.com/sdsrss/claudemd/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsrss%2Fclaudemd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34121022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["banned-vocab","claude-code","claude-code-plugin","code-quality","commit-lint","hooks","npm-cli","spec-enforcement"],"created_at":"2026-04-23T23:04:27.871Z","updated_at":"2026-06-09T19:00:31.263Z","avatar_url":"https://github.com/sdsrss.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# claudemd\n\n\u003e A **personal AI-coding discipline harness**: one developer's opinionated **AI-CODING-SPEC v6.14**, 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.\n\n[![CI](https://github.com/sdsrss/claudemd/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/sdsrss/claudemd/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/claudemd-cli.svg)](https://www.npmjs.com/package/claudemd-cli)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nclaudemd plugs into the Claude Code hook system to **block commits, pushes, and bash commands** that violate AI-CODING-SPEC v6.14 — 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.)\n\nA 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.\n\n\u003e **Status \u0026 scope.** This is a single-maintainer tool that encodes one developer's working discipline — not a general-purpose product. It's exercised almost entirely on my own repositories, the spec is opinionated, and the defaults reflect my preferences rather than a consensus. If you install it, expect to fork `CLAUDE.md` and tune the rules to your own workflow. Treat it as a reference implementation of \"spec-as-enforced-hooks,\" not a turnkey solution. Issues and PRs are welcome.\n\n---\n\n## Quick start\n\nRun **both** slash commands inside Claude Code:\n\n```\n/plugin marketplace add sdsrss/claudemd\n/plugin install claudemd@claudemd\n```\n\nThen bootstrap the **current** session (skip the wait-for-next-session restart) + verify:\n\n```\n/claudemd-install\n/claudemd-status\n/claudemd-doctor\n```\n\n`install` copies the spec into `~/.claude/`, writes the hook manifest, and evicts legacy entries — idempotent, safe to re-run. (Background: Claude Code does not fire `postInstall`, so without `/claudemd-install`, `install.js` runs on the next `SessionStart` instead.) `status` reports plugin version, shipped vs installed spec version, kill-switch state, and rule-hits row count. `doctor` runs 9+ health checks with `[✓] / [△] / [✗]` markers.\n\nFallback (no slash command — e.g. scripting outside CC): `node ~/.claude/plugins/cache/claudemd/claudemd/\u003cversion\u003e/scripts/install.js`. Find `\u003cversion\u003e` with `ls ~/.claude/plugins/cache/claudemd/claudemd/ | sort -V | tail -1`.\n\n\u003e ⚠️ **`~/.claude/CLAUDE.md` is shared real estate.** Claude Code reads this file as your user-global instructions across every project. If you've hand-written personal instructions there (`Always reply in 中文`, `My name is X`, etc.), install moves your existing files to `~/.claude/backup-\u003cISO\u003e/` (last 5 kept automatically) before writing the spec. Since v0.5.3, install prints a `[claudemd] WARN: …` line to stderr when the existing file does not look like a claudemd spec. To bring your personal instructions back on uninstall, run `CLAUDEMD_SPEC_ACTION=restore /claudemd-uninstall`.\n\n---\n\n## Prerequisites\n\n| Tool | Required | Why |\n|---|---|---|\n| `node \u003e= 20` | yes | install / status / doctor / update scripts (`package.json` engines) |\n| `jq` | yes | every hook parses Claude Code event JSON via `jq` — without it hooks silently fail-open |\n| `git` | yes | `ship-baseline-check` reads HEAD body; `session-start-check` runs `git ls-remote`; manual upgrade fallback |\n| `gh` | recommended | `ship-baseline-check` calls `gh run list` — if absent, the hook fail-opens silently and shipping on red CI is no longer blocked |\n| `coreutils` | macOS only | hooks need GNU `timeout`. Install with `brew install coreutils`, then prepend `$(brew --prefix coreutils)/libexec/gnubin` to your `PATH` |\n\nVerify in one command (Linux): `node --version \u0026\u0026 jq --version \u0026\u0026 gh --version \u0026\u0026 git --version \u0026\u0026 timeout --version | head -1`. macOS users can swap `timeout` for `gtimeout` if `coreutils` is bottle-installed without the `gnubin` shim.\n\n---\n\n## What it installs\n\n| Layer | Contents |\n|---|---|\n| 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` |\n| 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` |\n| 1 standalone CLI | `claudemd-cli lint` · `claudemd-cli audit` ([npm: `claudemd-cli`](https://www.npmjs.com/package/claudemd-cli)) |\n| Spec v6.14 | `~/.claude/CLAUDE.md` · `CLAUDE-extended.md` · `CLAUDE-changelog.md` · `OPERATOR.md` (backup-before-overwrite) |\n\nInstall moves any existing `~/.claude/CLAUDE*.md` to `~/.claude/backup-\u003cISO\u003e/` (last 5 kept automatically). Uninstall offers `keep / restore / delete`; `delete` requires an extra confirmation.\n\n\u003e Since v0.1.5, hook registration lives in the plugin's own `hooks/hooks.json` — the Claude Code harness expands `${CLAUDE_PLUGIN_ROOT}` there automatically on every invocation, so hooks track the active plugin version without manual re-registration. `install.js`'s remaining jobs are (1) copy `spec/CLAUDE*.md` into `~/.claude/` (with backup-before-overwrite), (2) evict any legacy claudemd hook entries from prior installs (≤0.1.1 absolute-path form, 0.1.2-0.1.4 `${CLAUDE_PLUGIN_ROOT}`-in-settings.json form), and (3) write the installed manifest. It never touches other-plugin hooks. Claude Code's plugin-lifecycle `postInstall` field is not honored, so the script runs from `SessionStart` instead.\n\n---\n\n## Hooks (what fires when)\n\nOnce installed, hooks run silently in the background. Verbose log: `~/.claude/logs/claudemd.jsonl` (one row per hook decision). Aggregate via `/claudemd-audit`.\n\n| Trigger | Hook | What happens |\n|---|---|---|\n| `git commit` with banned vocab (e.g. `significantly`, `70% faster`, `should work`) | `banned-vocab-check` | Blocks the commit with a message pointing to the §10-V spec rule. |\n| Bash command with `rm -rf $VAR` (unvalidated expansion) or unpinned `npx \u003cpkg\u003e` | `pre-bash-safety-check` (v0.5.0+) | Blocks at PreToolUse:Bash per §8 SAFETY. Bypass via `[allow-rm-rf-var]` / `[allow-npx-unpinned]` token in the command, or pin/validate the variable. |\n| `git push` while base-branch CI is red | `ship-baseline-check` | Blocks the push (2-second `gh run list` timeout; fail-open if `gh` absent or times out). |\n| Bash command matching ship/push/deploy/release with an unread matched `MEMORY.md` entry | `memory-read-check` | Blocks the command with a list of memory files to Read first. |\n| Session end with `~/.claude/tmp/` growth \u003e 20 entries | `residue-audit` | Advisory stderr warning; never blocks. |\n| Session end with fresh `tmp.XXXXXX`-style directories | `sandbox-disposal-check` | Advisory stderr warning. |\n| Stop event with `Why:`-less hard-rule citations in the assistant turn | `mem-audit` (v0.9.4+) | Detects spec-citation patterns missing the `Why:` rationale token; advisory log. |\n| Session end | `session-summary` (v0.8.0+) | Writes `~/.claude/.claudemd-state/last-session-summary.json`; banner emit at next `SessionStart`. |\n| New session start with GitHub remote tag newer than local cache max version | `session-start-check` (v0.4.0+) | Injects an \"upgrade available\" banner via `additionalContext`. Rate-limited to once per 24h via `~/.claude/.claudemd-state/upstream-check.lastrun` sentinel. 3-second `git ls-remote` timeout, fail-open. |\n| First `UserPromptSubmit` after a mid-session `/plugin install` upgrade | `version-sync` (v0.3.1+) | Backgrounds `install.js` once per session when the manifest version diverges from the active plugin's `package.json`. Sentinel-gated; fail-open. |\n| `PostToolUse` after assistant text containing banned vocab | `transcript-vocab-scan` | Advisory; logs to rule-hits without blocking. |\n| Session end with last assistant turn carrying §10 four-section out of order, `Done:` lines lacking evidence fingerprints, or `Uncertain:` short hedges without `because` | `transcript-structure-scan` (v0.9.10+) | Stop advisory — closes the audit gap that ~7 self-enforced HARD rules (§iron-law-2 / §10-four-section-order / §10-honesty) had no hook-side feedback signal. Opt-in (`TRANSCRIPT_STRUCTURE_SCAN=1`, default OFF) for FP signal collection; FP-tightened so single-section `Done:` lines never trigger. |\n\n### Execution order (PreToolUse:Bash)\n\nCC runs all configured PreToolUse hooks for `Bash` sequentially in declaration order. **First deny stops the rest** and the tool call is denied. The 4 Bash hooks fire in this order (declared in `hooks/hooks.json`):\n\n1. `pre-bash-safety-check` (§8 SAFETY immutable) — `rm -rf $VAR`, unpinned `npx \u003cpkg\u003e`. First so a §8 violation can never be overridden by a downstream hook.\n2. `banned-vocab-check` (§10-V) — `git commit` message scan.\n3. `ship-baseline-check` (§7) — `git push` while base CI is red.\n4. `memory-read-check` (§11) — ship/release/deploy commands matching unread MEMORY.md tags.\n\nPer-hook timeout (3-5s in `hooks.json`); timeout = treated as exit 0 (pass) per fail-open contract. Stop / SessionStart / UserPromptSubmit / PostToolUse hooks run all declared hooks regardless (none can block; advisories accumulate). Internal hook errors (missing `jq`, malformed event JSON, unreadable patterns file) fail-open; failures do NOT propagate to subsequent hooks.\n\n**Readonly fast-path** (v0.8.3 introduced opt-in default-OFF; **v0.20.0 promoted to default-ON** via §13.3 advisory→enforce gate): hooks 1, 2, and 4 short-circuit when the command is a definitely-read-only shape (no shell-meta, first token in safe-reader whitelist — `ls`, `cat`, `git log`, `git status`, `git diff`, `git rev-parse`, `pwd`, `echo`, `head`, `tail`, etc.). Hook 3 only fires on `git push` so the fast-path doesn't apply. Opt-out: `export BASH_READONLY_FAST_PATH=0` (or any other value than the literal `0` is treated as ON).\n\n## Commands\n\n| Command | Purpose |\n|---|---|\n| `/claudemd-install` | Bootstrap the current session right after `/plugin install` (copy spec into `~/.claude/`, write manifest, evict legacy entries). Idempotent. CC does not fire `postInstall`, so without this command `install.js` waits until the next `SessionStart`. |\n| `/claudemd-status [--verbose]` | Plugin version + spec version + kill-switch state + logs line count. `--verbose` adds per-hook env-var × event × effective vs persisted state table + 5 escape-token reference. |\n| `/claudemd-update` | Interactive diff against plugin-shipped spec, then apply-all or cancel (4-file spec set is lockstep — per-file select would dangle §EXT cross-references). |\n| `/claudemd-audit [--days N]` | Aggregate rule-hits over last N days (default 30). Top banned-vocab patterns, per-hook deny counts. |\n| `/claudemd-toggle \u003chook-name\u003e` | Enable/disable a specific hook by toggling `DISABLE_*_HOOK` in `settings.json` env. |\n| `/claudemd-doctor [--prune-backups=N]` | Health checks; optionally prune `~/.claude/backup-*` dirs older than N. v0.7.1+ also flags rule sections whose bypass:deny ratio \u003e 50% (R-N6 §0.1 demotion candidates). |\n| `/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). |\n| `/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. |\n| `/claudemd-clean-residue [--apply]` | Dry-run-by-default cleanup of stale `claudemd-sync-*` sentinels and historical `claudemd-(mockgh\\|work).*` test sandboxes. |\n| `/claudemd-uninstall` | Pre-uninstall cleanup: clears manifest + state + log + legacy `settings.json` hook entries. Run BEFORE `/plugin uninstall claudemd@claudemd` (see [Uninstall](#uninstall)). |\n\n---\n\n## Standalone CLI\n\nThe same `banned-vocab.patterns` source the in-CC hook uses is also exposed as a Node CLI for **git pre-commit hooks, GitHub Actions, and other agents** (Codex, Cursor, OpenClaw) — anywhere outside the Claude Code process.\n\n```bash\n# After npm publish (operator-driven, not part of plugin install):\nnpx claudemd-cli lint \"your commit message here\"\nnpx claudemd-cli lint --stdin \u003c message.txt\nnpx claudemd-cli audit ~/.claude/projects/\u003cencoded\u003e/\u003csession\u003e.jsonl\nnpx claudemd-cli audit transcript.jsonl --json\n\n# Dev mode (this repo, before npm publish):\nnode bin/claudemd-lint.js lint \"your commit message here\"\nnode bin/claudemd-lint.js audit ~/.claude/projects/\u003cencoded\u003e/\u003csession\u003e.jsonl\n```\n\n| Subcommand | Purpose |\n|---|---|\n| `lint \u003ctext\u003e` / `--stdin` | Scan commit-message text for §10-V banned vocab. Exit 0 clean / 1 hits. |\n| `audit \u003cjsonl-path\u003e` | Scan all assistant-text turns in a Claude Code transcript jsonl. Skips `@ratio` patterns by default (chat prose has different baseline conventions); pass `--include-ratio` to include them. |\n| `--json` | JSON output (machine-readable for CI). |\n| `--version` / `--help` | Standard. |\n\n**Pre-commit example (`.git/hooks/commit-msg`)**:\n\n```bash\n#!/usr/bin/env bash\nnpx claudemd-cli lint --stdin \u003c \"$1\" || exit 1\n```\n\nThe CLI does NOT depend on `~/.claude/` state — pure stateless input → stdout/stderr + exit code. Same enforcement, anywhere Node 20+ runs.\n\n---\n\n## Kill-switches\n\nAll visible in `/claudemd-status`. Three tiers:\n\n**1. Plugin-wide.** Short-circuits every hook before any logic runs:\n\n```bash\nexport DISABLE_CLAUDEMD_HOOKS=1\n```\n\n**2. Per-hook.** Disable one, leave others active:\n\n```bash\nexport DISABLE_BANNED_VOCAB_HOOK=1               # or\nexport DISABLE_PRE_BASH_SAFETY_HOOK=1            # or\nexport DISABLE_SHIP_BASELINE_HOOK=1              # or\nexport DISABLE_RESIDUE_AUDIT_HOOK=1              # or\nexport DISABLE_MEMORY_READ_HOOK=1                # or\nexport DISABLE_MEMORY_HINT_HOOK=1                # v0.11.0+ — UserPromptSubmit MEMORY.md tag pre-matcher (proactive §11 hint)\nexport DISABLE_SANDBOX_DISPOSAL_HOOK=1           # or\nexport DISABLE_SESSION_START_HOOK=1              # or\nexport DISABLE_SESSION_SUMMARY_HOOK=1            # v0.8.0+ — Stop hook writing summary\nexport DISABLE_USER_PROMPT_SUBMIT_HOOK=1         # version-sync (mid-session upgrade re-install)\nexport DISABLE_TRANSCRIPT_VOCAB_SCAN_HOOK=1      # PostToolUse §10-V advisory scan\nexport DISABLE_TRANSCRIPT_STRUCTURE_SCAN_HOOK=1  # v0.9.10+ — Stop §10 four-section advisory\nexport DISABLE_MEM_AUDIT_HOOK=1                  # v0.9.4+ — Stop Why:-less citation advisory\nexport DISABLE_SESSION_END_CHECK_HOOK=1          # v0.9.27+ — SessionEnd §11-session-exit mid-SPINE check\nexport DISABLE_SESSION_EXTENDED_READ_HOOK=1      # v0.10.1+ — PreToolUse:Read §13.1-extended-read denominator signal\nexport DISABLE_MID_SPINE_YIELD_HOOK=1            # v0.15.0+ — Stop §11-mid-spine-yield advisory (opt-in via MID_SPINE_YIELD_SCAN=1)\n```\n\n**2a. Per-sub-feature** (v0.4.0+). Sub-flags inside an enabled hook, named without the `_HOOK` suffix:\n\n```bash\nexport DISABLE_UPSTREAM_CHECK=1            # only the upstream-tag-check sub-feature\n                                           # of session-start-check; bootstrap-on-mismatch\n                                           # behavior remains active.\n\nexport DISABLE_SESSION_SUMMARY_BANNER=1    # v0.8.0+ — only the SessionStart banner-emit\n                                           # half of session-summary; the Stop-side write\n                                           # of last-session-summary.json continues so\n                                           # the data is captured for /claudemd-audit\n                                           # but no additionalContext line is injected.\n\nexport DISABLE_BATCH_CADENCE_ADVISORY=1    # v0.19.2+ — only the §13.2 batch-review\n                                           # cadence advisory inside session-end-check;\n                                           # mid-SPINE warn-on-unvalidated-mutation\n                                           # behavior remains active. Threshold also\n                                           # configurable via CLAUDEMD_BATCH_THRESHOLD=N\n                                           # (positive integer, default 20).\n\nexport BANNED_VOCAB_PROSE_SCAN=0           # v0.21.0+ — disable only the Path 2 prose\n                                           # scan in banned-vocab-check (the v0.21.0\n                                           # §13.3 Gate 2 promotion that denies\n                                           # ship-flow commands when the preceding\n                                           # assistant turn's prose contains a\n                                           # high-fire §10-V pattern). Path 1\n                                           # commit-message scan remains active.\n\nexport CLAUDEMD_PATH2_DRY_RUN=1            # v0.21.1+ — Path 2 observability mode.\n                                           # When set, ship-verb + prior-prose §10-V\n                                           # match logs a `deny-prose-dry-run` event\n                                           # row to ~/.claude/logs/claudemd.jsonl\n                                           # instead of denying. Grep the rows to\n                                           # measure true-positive vs false-positive\n                                           # rate before committing to live deny.\n                                           # Sample: jq -r 'select(.event==\"deny-prose-dry-run\") | .extra.matched' ~/.claude/logs/claudemd.jsonl\n```\n\n**3. Per-invocation escape hatches.** Embed in the command itself, no env var needed:\n\n| Escape | Where | Bypasses |\n|---|---|---|\n| `[allow-banned-vocab]` | commit message | `banned-vocab-check` |\n| `known-red baseline: \u003creason\u003e` | commit body | `ship-baseline-check` |\n| `[skip-memory-check]` | bash command string | `memory-read-check` |\n| `[allow-rm-rf-var]` | bash command string | `pre-bash-safety-check` (rm-with-var path only) |\n| `[allow-npx-unpinned]` | bash command string | `pre-bash-safety-check` (unpinned npx path only) |\n\n---\n\n## Update\n\nClaude Code has **no** `/plugin update` slash command — it's silently ignored as unrecognized. The canonical upgrade sequence is:\n\n```\n/plugin marketplace update claudemd                 # refresh local marketplace clone (git fetch)\n/plugin uninstall claudemd@claudemd                 # remove old plugin version\n/plugin install claudemd@claudemd                   # install latest from refreshed clone\n/reload-plugins                                     # apply changes to current session\n```\n\nOr open the interactive UI via `/plugin` → **Installed** tab → select `claudemd` → follow upgrade prompts.\n\nAfter the plugin upgrade, sync the shipped spec into `~/.claude/`:\n\n```\n/claudemd-update\n```\n\nThe command prints per-file diff summary, then prompts `apply-all` or `cancel`. Per-file select is intentionally not supported — the 4-file spec set (`CLAUDE.md` + `CLAUDE-extended.md` + `CLAUDE-changelog.md` + `OPERATOR.md`) evolves lockstep, and mixing versions would dangle `§EXT §X-EXT` cross-references in Core. Backup is automatic (retained to 5). `/claudemd-update` never fetches from GitHub — it only diffs the plugin-cache spec against your `~/.claude/CLAUDE*.md` + `~/.claude/OPERATOR.md`. The network fetch is Claude Code's job (via `/plugin marketplace update`).\n\n---\n\n## Uninstall\n\nClaude Code's marketplace lifecycle does not fire `preUninstall`, so `/plugin uninstall claudemd@claudemd` alone leaves orphan state behind (`~/.claude/.claudemd-manifest.json`, `~/.claude/.claudemd-state/`, `~/.claude/logs/claudemd.jsonl`). Use the **two-step flow**:\n\n```\n/claudemd-uninstall                    # clear manifest + state + log (plugin still installed)\n/plugin uninstall claudemd@claudemd    # CC removes plugin cache itself\n```\n\nReversing the order is the orphan-state vector — `${CLAUDE_PLUGIN_ROOT}` and `scripts/uninstall.js` are gone after `/plugin uninstall`, with no in-tree tool to clean up afterwards. `/claudemd-doctor` flags `[△] plugin cache: orphan manifest …` if you've already hit this.\n\n### Spec disposition\n\n`/claudemd-uninstall` defaults to `keep` (leaves `~/.claude/CLAUDE*.md` in place). Override via env vars before the slash command:\n\n| Option | Env vars | Behavior |\n|---|---|---|\n| `keep` (default) | (none) | `~/.claude/CLAUDE*.md` left in place; settings.json hook entries cleared. |\n| `restore` | `CLAUDEMD_SPEC_ACTION=restore` | Copies the most recent `~/.claude/backup-\u003cISO\u003e/*.md` back to `~/.claude/`. Use this if your install-time stderr showed `[claudemd] WARN: existing ~/.claude/CLAUDE.md does not look like a claudemd spec` — your hand-written user-global instructions are sitting in the backup waiting to be brought back. |\n| `delete` | `CLAUDEMD_SPEC_ACTION=delete CLAUDEMD_CONFIRM=1` | Hard-AUTH: removes all four spec files (`CLAUDE*.md` + `OPERATOR.md`). |\n\n`CLAUDEMD_PURGE=1` (env var) on `/claudemd-uninstall` also drops `~/.claude/.claudemd-state/` and your rule-hits log.\n\n### Direct script invocation (advanced fallback)\n\nIf `/claudemd-uninstall` is unavailable (you already ran `/plugin uninstall` first and want to clean up by reaching into the cache before it gets pruned, or you need to script the uninstall outside CC):\n\n```bash\nCLAUDEMD_SPEC_ACTION=keep     node ~/.claude/plugins/cache/claudemd/claudemd/\u003cversion\u003e/scripts/uninstall.js\nCLAUDEMD_SPEC_ACTION=restore  node ~/.claude/plugins/cache/claudemd/claudemd/\u003cversion\u003e/scripts/uninstall.js\nCLAUDEMD_SPEC_ACTION=delete CLAUDEMD_CONFIRM=1 node ~/.claude/plugins/cache/claudemd/claudemd/\u003cversion\u003e/scripts/uninstall.js\n```\n\nThe slash command and the script are equivalent — the slash command just supplies `${CLAUDE_PLUGIN_ROOT}` for you.\n\n---\n\n## Troubleshooting\n\n**`Plugin \"claudemd\" not found in any marketplace`** — you forgot the `/plugin marketplace add sdsrss/claudemd` step. Re-run it, then retry install.\n\n**Hooks don't fire / `~/.claude/CLAUDE*.md` not present after install** — Claude Code's `postInstall` lifecycle is not honored, so `install.js` runs from the `SessionStart` hook on your next session, not at install time. Three options:\n\n1. **Recommended**: run `/claudemd-install` in your current session. Wraps `scripts/install.js` exactly the same way `SessionStart` does — idempotent, prints a JSON summary.\n2. Start a fresh Claude Code session (`SessionStart` fires the bootstrap automatically).\n3. Run the script manually outside CC (e.g. shell scripting): `node ~/.claude/plugins/cache/claudemd/claudemd/\u003cversion\u003e/scripts/install.js` (find `\u003cversion\u003e` with `ls ~/.claude/plugins/cache/claudemd/claudemd/ | sort -V | tail -1`).\n\nVerify with `/claudemd-status` — the \"log.lines\" count should increment after the next hook fires.\n\n**`/plugin update claudemd` does nothing / empty stdout** — `/plugin update` is not a valid Claude Code slash command; CC silently ignores unrecognized commands. Use the canonical sequence in the [Update](#update) section: `/plugin marketplace update claudemd` → `/plugin uninstall claudemd@claudemd` → `/plugin install claudemd@claudemd` → `/reload-plugins`. If that also fails (marketplace clone refuses to refresh), manually `git -C ~/.claude/plugins/marketplaces/claudemd fetch origin main --tags \u0026\u0026 git merge --ff-only origin/main`, then `git archive v\u003cversion\u003e | tar -x -C ~/.claude/plugins/cache/claudemd/claudemd/\u003cversion\u003e/`, then run that version's `scripts/install.js`.\n\n**`Hook command references ${CLAUDE_PLUGIN_ROOT} but the hook is not associated with a plugin`** (5 errors on every `Bash` tool call + every session end) — you're on claudemd 0.1.2 / 0.1.3 / 0.1.4. Those releases wrote hook commands into `~/.claude/settings.json` under the literal `${CLAUDE_PLUGIN_ROOT}` token, but the CC harness only expands that variable for hooks defined in a plugin's own `hooks/hooks.json` — never in `settings.json`. The fix is v0.1.5+, which moves hook registration into the plugin's `hooks/hooks.json` (where the token expands correctly) and evicts the stale settings.json entries on install. Upgrade via the canonical sequence in the [Update](#update) section, then restart the Claude Code session to clear the cached hook registry.\n\n**`ship-baseline-check` silently passes on red CI** — `gh` CLI is not installed, or authentication failed. Install with `brew install gh` / `apt-get install gh` and run `gh auth login`. Check with `/claudemd-doctor` — it reports `gh: missing` if absent.\n\n**CI matrix fails on macOS** — our own CI installs `coreutils` for GNU `timeout`. If you're running hooks outside the bundled CI, ensure `timeout` is on PATH (`brew install coreutils \u0026\u0026 export PATH=\"$(brew --prefix coreutils)/libexec/gnubin:$PATH\"`).\n\n**`/claudemd-doctor` reports backup growth** — run `/claudemd-doctor --prune-backups=5` to keep only the 5 most recent.\n\n**`PreToolUse:Bash hook error ... No such file or directory`** pointing at `~/.claude/hooks/banned-vocab-check.sh` — Claude Code loaded `settings.json` at session start and cached the old hand-install hook entry in memory. `install.js` migrated the on-disk entry to the cache path and moved the original shell file to `~/.claude/backup-*/hooks/`, but the running session's hook registry is still stale. Exit and restart the Claude Code session — settings.json is re-read from disk, and the error stops. (This applies to any mid-session `settings.json` change, not just claudemd.)\n\n---\n\n## Project layout\n\n```\nclaudemd/\n├── .claude-plugin/\n│   ├── plugin.json           # minimal manifest (name, version, author, license, keywords)\n│   └── marketplace.json      # marketplace catalog entry\n├── hooks/                    # 16 shell hooks + hooks/lib/ (hook-common, rule-hits, platform)\n│   └── hooks.json            # authoritative hook registration (v0.1.5+); CC expands ${CLAUDE_PLUGIN_ROOT} here\n├── commands/                 # 12 slash-command markdown files\n├── bin/                      # standalone CLI entrypoint (claudemd-lint.js → `npx claudemd-cli` on npmjs.org)\n├── scripts/                  # 16 Node.js management scripts + scripts/lib/ (single-source registry, lint, etc.)\n├── spec/                     # shipped v6.14.1 CLAUDE*.md trio + OPERATOR.md + hard-rules.json manifest\n├── tests/                    # hook shell tests + Node.js tests + integration + fixtures\n├── docs/                     # ADDING-NEW-HOOK.md + RULE-HITS-SCHEMA.md + superpowers/\n└── .github/workflows/        # ci.yml (ubuntu+macOS × node 20) + npm-publish.yml (tag-triggered)\n```\n\n---\n\n## Extending\n\n- **Add a new hook** — see [`docs/ADDING-NEW-HOOK.md`](docs/ADDING-NEW-HOOK.md) for the 5-step guide (hook script + test + plugin registration + doc + version bump).\n- **Rule-hits log schema** — [`docs/RULE-HITS-SCHEMA.md`](docs/RULE-HITS-SCHEMA.md) for the JSONL row format used by `/claudemd-audit`.\n- **Design rationale + decisions log** — [`docs/superpowers/specs/2026-04-21-claudemd-plugin-design.md`](docs/superpowers/specs/2026-04-21-claudemd-plugin-design.md).\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdsrss%2Fclaudemd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsdsrss%2Fclaudemd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdsrss%2Fclaudemd/lists"}