Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/docs-hygiene/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "docs-hygiene",
"version": "0.14.5",
"version": "0.14.6",
"description": "Documentation-hygiene toolkit: compress (flavor-trim markdown with a semantic-diff safety net), audit-noise (classify markdown noise), extract-ssot (deduplicate repeated content into a single source of truth), audit-encapsulation (detect citations into skill-private surfaces), rename-references (sweep stale references after renames), and audit-derivability (classify whether a whole document earns its existence \u2014 could a fresh agent re-derive it from the code?).",
"author": {
"name": "Melodic Software",
Expand Down
18 changes: 18 additions & 0 deletions plugins/docs-hygiene/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog — docs-hygiene plugin

## [0.14.6]

### Added

- **Shared clean-tree / no-scope fallback contract**
(`context/clean-tree-fallback.md`): the offer → confirm → prescribed-defaults
→ decline-or-silence-no-op skeleton that audit-noise 0.12.0 introduced, now
cited by `audit-noise`, `audit-derivability`, `audit-encapsulation`,
`compress`, and `extract-ssot`, with deliberate divergences recorded
(compress stays mutating/audit-first; encapsulation triggers on "no inherited
scope", not only a clean tree).

### Changed

- **compress `audit` on a clean tree** offers the confirmation-gated free
corpus audit (report-only) instead of the friendly no-op, matching the
sibling audit skills (#2734).

## [0.14.5]

### Fixed
Expand Down
4 changes: 4 additions & 0 deletions plugins/docs-hygiene/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ upkeep.

## How the skills adapt to your repo

Bare invocations with no target share a confirmation-gated clean-tree /
no-scope fallback (`context/clean-tree-fallback.md`): offer a corpus run with
prescribed defaults, never auto-start, and no-op on decline or silence.

<!-- markdown-discipline-ignore -->
The bundled defaults are repo-agnostic: detectors run against the repository
they are invoked in, output destinations default to conventional locations
Expand Down
57 changes: 57 additions & 0 deletions plugins/docs-hygiene/context/clean-tree-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Clean-tree / no-scope fallback — shared contract

SSOT for how docs-hygiene audit skills behave when invoked with **no target and
no inherited working set**. Each skill cites this file and keeps only its
skill-specific prescribed defaults in its own `SKILL.md`.

## Shared shape (all audit skills)

When the invocation is bare — empty arg, clean tree (or no inherited scope),
and nothing in the conversation already naming a corpus — the skill:

1. **Reports** that no default local target exists (uncommitted `.md` empty, or
no inherited detect scope).
2. **Offers** a confirmation-gated escalation to a repo-wide (or skill-default)
corpus run — never starts it unprompted.
3. Presents **prescribed defaults** (overridable) so a bare "yes" suffices.
4. On **decline or silence**, ends as the friendly no-op (skill-specific exit
message). Unattended / non-interactive sessions surface the offer as blocked
and stop — never launch the repo-wide run on silence.
5. An **explicit opt-in** keyword (`sweep`, `audit` with an explicit corpus
flag, or a user-stated "whole repo") skips the confirmation and runs.

This is the audit-noise 0.12.0 clean-tree fallback generalized. Skills that
already had a close cousin (derivability's empty-target escalation,
encapsulation's no-scope confirmation, compress's interview fallback) converge
here rather than drifting.

## Who participates

| Skill | Trigger | Explicit opt-in that skips confirm | Notes |
|---|---|---|---|
| `audit-noise` | empty arg + clean tree | user-stated whole-repo / confirmed offer | Read-only; report-first; chunked `detect.sh` |
| `audit-derivability` | empty arg + clean tree | `sweep <dir\|repo>` | Read-only; confirmed escalation runs as `sweep` |
| `audit-encapsulation` | bare detect with no inherited scope | `sweep` | Domain is already repo-wide; confirm is about intent, not discovery |
| `compress` (default + `audit`) | empty arg + clean tree, interactive | user-stated whole-repo / confirmed offer | Mutating default stays interview-gated after a free audit pass; bare `compress audit` on a clean tree offers the same free audit corpus (report-only) instead of no-opping |
| `extract-ssot` | bare invocation with no scope | path/glob-scoped survey after confirm | Already documented as "Bare invocation — confirm scope first"; cites this shape |
Comment thread
kyle-sexton marked this conversation as resolved.
| `rename-references` | *(out of scope)* | — | Always needs an old/new token pair; no clean-tree corpus offer |

## Deliberate divergences (do not "fix" these away)

- **compress is mutating.** Its clean-tree path is audit-first then a second
confirmation before any Edit. Read-only siblings stop after the report.
- **encapsulation's trigger is "no inherited scope", not only "clean tree".**
A dirty tree with unrelated edits still needs the confirm when nothing
names the detect surface.
- **Prescribed defaults differ by skill** (fixture exclusions, concurrency,
spot-test caps, report-vs-fix). Those knobs stay in each skill's
`SKILL.md`; this file owns only the offer/confirm/no-op skeleton.
- **Non-interactive contexts** (subagent, headless/CI): no-op / blocked
offer — never auto-escalate. Compress states this explicitly; the others
inherit it from step 4 above.

## Citation

Skill bodies point here with a one-line cross-ref under their auto-detect /
no-scope section, e.g. "Shared shape: `../../context/clean-tree-fallback.md`
(plugin root)." Paths are relative to the citing `SKILL.md`.
2 changes: 2 additions & 0 deletions plugins/docs-hygiene/skills/audit-derivability/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ One action per response; actions do not chain implicitly. `sweep` is the only re

## Auto-detect default

Shared clean-tree / no-scope shape: [`../../context/clean-tree-fallback.md`](../../context/clean-tree-fallback.md).

1. Empty arg AND clean tree → no default target exists. Report that, then OFFER escalation to a repo-wide corpus sweep — never start it unprompted. Confirm with the user first (via `AskUserQuestion` where available, a plain prose question otherwise), presenting the prescribed defaults below pre-filled so a bare "yes" suffices; the interview may adjust any knob. Declining, or no answer, ends as the friendly no-op exit 0 ("No uncommitted .md files. Pass a file/dir target, or `sweep <dir>` for a corpus.")
2. Empty arg AND uncommitted `.md` files → audit those files
3. Single file path → single-document audit
Expand Down
4 changes: 4 additions & 0 deletions plugins/docs-hygiene/skills/audit-encapsulation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ One action per response.

### No inherited scope — confirm before a repo-wide run

Shared clean-tree / no-scope shape: [`../../context/clean-tree-fallback.md`](../../context/clean-tree-fallback.md).
This skill's trigger is "no inherited scope" (not only "clean tree") — a dirty tree with unrelated
edits still needs the confirm when nothing names the detect surface.

The default action's domain is the whole repo, but a bare invocation does not prove the user meant
that. When the invocation arrives with NO inherited working set — no diff in flight, no prior audit
notes, nothing in the conversation narrowing the scope — ask ONE confirmation before running the
Expand Down
2 changes: 2 additions & 0 deletions plugins/docs-hygiene/skills/audit-noise/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Single action v1; `relocate` and `generalize` actions are deferred until real de

## Auto-detect default

Shared clean-tree / no-scope shape: [`../../context/clean-tree-fallback.md`](../../context/clean-tree-fallback.md).

1. Empty arg AND clean tree → OFFER the repo-wide audit instead of silently no-opping; run only on
the user's confirmation. The offer carries prescribed defaults (overridable): corpus = all
tracked `.md` minus `**/evals/fixtures/**` and `CHANGELOG.md`; slice-scoped files (contract and
Expand Down
10 changes: 6 additions & 4 deletions plugins/docs-hygiene/skills/compress/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,21 @@ Flags (apply to both actions):

## Auto-detect default

Shared clean-tree / no-scope shape: [`../../context/clean-tree-fallback.md`](../../context/clean-tree-fallback.md).

1. Empty arg AND clean tree → interactive session: repo-wide interview fallback (next section); non-interactive context (subagent, headless/CI): friendly no-op exit 0 ("No uncommitted .md files. Pass file/dir target.")
2. Empty arg AND uncommitted `.md` files → batch default action over those files
3. Single file path → single-file default action
4. Directory path → batch default action (filenames sorted lexically for deterministic output)
5. First positional == `audit` → audit action on rest
5. First positional == `audit` → audit action on rest (same clean-tree offer as rule 1 when the rest is empty — report-only corpus audit, no Edit)

## Repo-wide interview fallback (empty arg, clean tree, interactive)

Instead of dead-ending, offer a repo-wide run — confirmation-gated at every step; declining at any step exits with the friendly no-op message.
Instead of dead-ending, offer a repo-wide run — confirmation-gated at every step; declining at any step exits with the friendly no-op message. Bare `/docs-hygiene:compress audit` on a clean tree uses steps 1–2 only (free audit + report; no compression interview).

1. **Offer** (AskUserQuestion): run against all tracked eligible `.md` files? Decline → no-op exit.
2. **Audit first** (free — mechanical scan, no subagents): run the audit action over every tracked eligible `.md`. Present INLINE only aggregate counts per class, a dispatch-cost estimate (2 subagent requests per compressed file), and a top-20 excerpt of COMPRESS rows selected deterministically: expected-yield band descending, then word count descending, then lexical path (band strings tie; the two tie-breaks keep the excerpt stable run-to-run). Write the full per-file table to a file — lexically sorted per the "Summary output deterministic" hard rule — and point at it. Never render every row inline — on a large repo the full table can run to hundreds of KB and truncate the confirmation prompt it feeds.
3. **Interview with prescribed defaults** (AskUserQuestion, recommended option listed first):
2. **Audit first** (free — mechanical scan, no subagents): run the audit action over every tracked eligible `.md`. Present INLINE only aggregate counts per class, a dispatch-cost estimate (2 subagent requests per compressed file), and a top-20 excerpt of COMPRESS rows selected deterministically: expected-yield band descending, then word count descending, then lexical path (band strings tie; the two tie-breaks keep the excerpt stable run-to-run). Write the full per-file table to a file — destination `${CLAUDE_PLUGIN_DATA}/audit/<branch-or-scope>-audit.md` when that dir is writable, otherwise a temp path echoed to the user — lexically sorted per the "Summary output deterministic" hard rule — and point at it. Never render every row inline — on a large repo the full table can run to hundreds of KB and truncate the confirmation prompt it feeds. **Stop here when the invocation was the audit action** (report-only).
Comment thread
kyle-sexton marked this conversation as resolved.
3. **Interview with prescribed defaults** (AskUserQuestion, recommended option listed first) — default (mutating) action only:
- **Scope** — default: all COMPRESS-classified files, highest expected yield first; alternates: top-N highest-yield subset, include UNCERTAIN, stop after audit (report only).
- **Concurrency** — default: 2 concurrent subagents per wave (rate-limit-conservative); alternates: 1 (sequential), 3-5 (`context/fan-out-orchestration.md` default).
- **Always-loaded files** — default: excluded (SKIP per the 2-3% empirical baseline); including them requires the same explicit opt-in as `--force`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Per `../SKILL.md` "Auto-detect default", argument resolution at invocation:
| `/docs-hygiene:compress <file.md>` | single file | default action, single-file |
| `/docs-hygiene:compress <dir>` | every `.md` under `<dir>` (recursive); filenames sorted lexically for determinism | default action, batch |
| `/docs-hygiene:compress audit` (empty rest) AND uncommitted `.md` exist | files from `git status --porcelain` matching `*.md` | audit action over each |
| `/docs-hygiene:compress audit` (empty rest) AND clean tree | (none) | friendly no-op exit 0 |
| `/docs-hygiene:compress audit` (empty rest) AND clean tree | interactive: all tracked eligible `.md` offered via the repo-wide interview fallback steps 1–2 (report-only); non-interactive: (none) | interactive: confirmation-gated free audit corpus; non-interactive: friendly no-op exit 0 |
| `/docs-hygiene:compress audit <file.md>` | single file | audit action |
| `/docs-hygiene:compress audit <dir>` | every `.md` under `<dir>`; lexical sort | audit action, batch |

Expand Down
2 changes: 2 additions & 0 deletions plugins/docs-hygiene/skills/extract-ssot/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ One action per response; actions don't chain implicitly.

## Bare invocation — confirm scope first

Shared clean-tree / no-scope shape: [`../../context/clean-tree-fallback.md`](../../context/clean-tree-fallback.md).

A bare `/docs-hygiene:extract-ssot` with no working notes to resume, no
argument, and no scope implied by the conversation does **not**
auto-dispatch the exhaustive survey. Exhaustive `identify` sweeps every
Expand Down
Loading