diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6abad4b..b4565de 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -24,7 +24,7 @@ "name": "pr-flow", "source": "./plugins/pr-flow", "description": "PR review feedback loop for Claude Code. Create PRs with readiness checks, commit + push + trigger @claude review, inspect status, and work through review issues interactively.", - "version": "1.2.0" + "version": "1.2.1" } ] } diff --git a/plugins/pr-flow/.claude-plugin/plugin.json b/plugins/pr-flow/.claude-plugin/plugin.json index f9419c6..bd80772 100644 --- a/plugins/pr-flow/.claude-plugin/plugin.json +++ b/plugins/pr-flow/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "pr-flow", "description": "PR review feedback loop for Claude Code. Create PRs with readiness checks, commit + push + trigger @claude review, inspect status, and work through review issues interactively.", - "version": "1.2.0", + "version": "1.2.1", "author": { "name": "gering" }, diff --git a/plugins/pr-flow/README.md b/plugins/pr-flow/README.md index 891c083..aaada8c 100644 --- a/plugins/pr-flow/README.md +++ b/plugins/pr-flow/README.md @@ -46,7 +46,7 @@ If a clear pattern emerges (e.g. 18 of 20 last PRs were squashed), `/merge` sugg ### Pre-merge documentation check -**What it does.** Before executing a merge, `/merge` runs a readiness pass: checks the README for staleness, verifies a version bump happened if the branch mentions version changes, checks for a changelog entry, checks knowledge-system entries for new patterns. On warnings, it asks: `[f]ix / [m]erge anyway / [a]bort`. +**What it does.** Before executing a merge, `/merge` runs a readiness pass: checks the README for staleness, verifies a version bump happened if the branch mentions version changes, checks for a changelog entry, checks knowledge-system entries for new patterns. On warnings, it asks: `[f]ix / [m]erge anyway / [a]bort`. The same four checks run pre-PR in `/open` — both skills read their definitions from a **single shared spec** (`docs/READINESS-CHECKS.md`), so the heuristics can't drift apart. Only the stage behavior differs: `/open` auto-resolves fixes in place, `/merge` inspects read-only and defers the fix to the `[f]` choice. **Why it matters.** The right moment to update docs is at merge time, not "later." The three-way prompt keeps you in the loop without being paternalistic. diff --git a/plugins/pr-flow/docs/READINESS-CHECKS.md b/plugins/pr-flow/docs/READINESS-CHECKS.md new file mode 100644 index 0000000..f183428 --- /dev/null +++ b/plugins/pr-flow/docs/READINESS-CHECKS.md @@ -0,0 +1,133 @@ +# Shared Documentation-Readiness Checks + +> Canonical definitions for the four documentation-readiness checks shared by +> `/open` (pre-PR, step 3c–3f) and `/merge` (pre-merge, step 8): README +> freshness, version bump, changelog, knowledge/conventions. Defines the +> detection signals, the ✅/⚠️/❌/➖ semantics, and the auto-fixable vs. manual +> split. Consumers add only their stage-specific behavior — they do not +> redefine the checks here. + +## Status semantics + +Every check resolves to exactly one of: + +- ✅ **pass** — requirement met, or correctly not applicable to *these* changes +- ⚠️ **warning** — a gap that needs a decision (auto-fix or accept). These + doc checks emit ⚠️, never ❌ — a stale doc never hard-blocks. +- ➖ **N/A** — the project doesn't use this convention; nothing to check +- ❌ **blocker** — reserved; the documentation checks do not produce blockers + +## Shared inputs + +All four checks read the branch diff against the base: + + git diff origin/...HEAD --name-only + +- **User-facing changes** — the diff touches user-visible code (`src/`, + `lib/`, `plugins/`, `skills/`, public entry points), or commit messages + describe `feat` / `fix` / `breaking`. +- **Internal only** — tests, configs, refactors, comments. Internal-only + changes resolve every check below to ✅ ("no action needed"). + +## The checks + +### 3c · README freshness — *manual* + +- **Signal:** user-facing code changed, but `README.md` itself was not + touched. Other `*.md` files (CONTRIBUTING, CHANGELOG, …) do **not** count as + a README update. +- ✅ `README.md` was touched, or changes are internal only +- ⚠️ "Code changed but docs untouched — README may be stale" +- ➖ no `README.md` exists +- **Classification: manual** — prose can't be auto-written; needs human + judgment about what to document. + +### 3d · Version bump — *auto-fixable* + +- **First detect whether the project versions releases** (warn only if it + does). Any of: + - `package.json` / `plugin.json` / `Cargo.toml` / `pyproject.toml` / + `*.csproj` with a version field that changed in the last ~10 commits + - semver git tags: `git tag --sort=-v:refname | head -5` + - `.changeset/`, `release-please` config, or similar release automation +- **No versioning signal** → ➖ "N/A (project does not appear to version + releases)" +- **Versioned** — check the diff for a bump: + `git diff origin/...HEAD -- '**/package.json' '**/plugin.json' '**/Cargo.toml' '**/pyproject.toml'` + - ✅ version was bumped — "Version bumped to " + - ✅ not bumped, changes internal only — "No bump needed (internal)" + - ⚠️ not bumped, changes user-facing → "Version bump may be needed. + Detected: . Suggest: " +- **Monorepo / multi-package awareness:** if several version files exist + (e.g. each plugin's `plugin.json` + a root `marketplace.json`), check which + one(s) the diff affects and remind per package — keep them in sync. +- Respect repo-specific semver conventions from memory / CLAUDE.md (e.g. + "patch for small changes, minor for new features"). +- **Classification: auto-fixable** — bump the detected field(s) per the + suggestion; update every in-sync file together. + +### 3e · Changelog — *auto-fixable* + +- **Detect presence:** `CHANGELOG.md`, `CHANGELOG`, `HISTORY.md`, + `RELEASES.md`, `docs/changelog/`, `.changeset/` +- **None** → ➖ "N/A (no changelog)" +- **Exists** — check whether it was updated on this branch: + `git diff origin/...HEAD --name-only | grep -iE '(changelog|history|releases|\.changeset/)'` + - ✅ updated, or changes internal only + - ⚠️ not updated, changes user-facing → "Changelog unchanged — add an + entry for this PR". Suggest the section from the change type: `feat` → + Added, `fix` → Fixed, `breaking` → Changed/Removed. +- **Link to version bump (3d):** if the version bumped but the changelog + didn't (or vice versa), flag the inconsistency explicitly. +- **Classification: auto-fixable** — draft an entry under the unreleased / + next-version heading (use Keep-a-Changelog sections if that format is + detected from headings). + +### 3f · Knowledge / conventions — *auto-fixable* + +- **Detect a conventions/knowledge location** (system-agnostic — any of): + - `.claude/knowledge/`, `.claude/rules/` (gering `knowledge-system`) + - `.cursor/rules/`, `.cursorrules` (Cursor) + - `.github/copilot-instructions.md` (Copilot) + - `AGENTS.md`, `CONVENTIONS.md`, `CONTRIBUTING.md` + - `docs/adr/`, `docs/decisions/` (ADRs) + - `CLAUDE.md` with documented conventions beyond setup +- **None** → ➖ "N/A (no knowledge/convention system detected)" +- **Exists** — does the branch introduce **new patterns, conventions, or + generalizable fixes**? Heuristic: commit messages like "add ", + "refactor to ", "fix ", or many similar files + changed the same way. + - ✅ knowledge location was touched — "Conventions documented" + - ✅ nothing generalizable — "No new patterns to capture" + - ⚠️ new patterns likely but knowledge location untouched → "Knowledge gap" +- **Auto-fix target:** if `knowledge-system` is detected, invoke `/curate` + with the detected pattern; otherwise append to the closest topical file + (fallback: the generic `AGENTS.md` / `CONVENTIONS.md`). +- **Classification: auto-fixable** — unless the new pattern *conflicts* with + an existing documented rule, which needs judgment (then it's a manual ⚠️). + +## Auto-fixable vs. manual — summary + +| Check | Classification | Auto-fix action | +|---|---|---| +| 3c README freshness | manual | — (human writes the prose) | +| 3d Version bump | auto-fixable | bump version field(s); keep multi-package in sync | +| 3e Changelog | auto-fixable | draft entry under unreleased / next-version heading | +| 3f Knowledge / conventions | auto-fixable | `/curate`, or append to closest knowledge file | + +## How consumers apply this + +The checks above are identical for both skills. **Only the stage behavior +differs** — that, and nothing else, lives in the skills: + +- **`/open` step 3c–3f (pre-PR) — auto-resolve in place.** Per its + "auto-resolve warnings where feasible" principle, `/open` applies each + **auto-fixable** fix *immediately during the check phase* (bump the + version, draft the changelog entry, `/curate` the knowledge gap) so it + rarely surfaces as a warning. Only **manual** gaps (README) and fixes that + genuinely need judgment reach the step-4 decision as ⚠️. +- **`/merge` step 8 (pre-merge) — read-only.** `/merge` **never mutates + here.** It collects every ⚠️ into `DOC_WARNINGS` (tagged auto-fixable vs. + manual) and surfaces them in the final plan (step 13), where the user picks + `[f]` fix / `[m]` merge anyway / `[a]` abort. Auto-fix runs only after `[f]` + — and then commits locally + hands off to `/cycle`, never pushing directly. diff --git a/plugins/pr-flow/skills/merge/SKILL.md b/plugins/pr-flow/skills/merge/SKILL.md index 98d21d5..2deccd4 100644 --- a/plugins/pr-flow/skills/merge/SKILL.md +++ b/plugins/pr-flow/skills/merge/SKILL.md @@ -84,15 +84,17 @@ user_invocable: true - Count open blocking issues - If > 0 → ⚠️ warn explicitly, require explicit confirmation before merging. **Do not silently ignore.** -8. **Documentation readiness** (read-only at this stage — mirrors `/open` checks 3c-3f): - - Compare branch diff against base: `git diff origin/...HEAD --name-only` - - **README freshness**: user-visible code changed (`src/`, `lib/`, `plugins/`, `skills/`, public entry points) but `README.md` itself was not touched → ⚠️ "README may be stale" (manual — cannot auto-write). Changes to other `*.md` files (CONTRIBUTING, CHANGELOG, etc.) do not count as a README update. - - **Version bump**: if project versions releases (`package.json`/`plugin.json`/`Cargo.toml`/`pyproject.toml` with version field + git tags or recent bump commits) AND no version field bumped on this branch AND changes look user-facing (feat/fix/breaking from commit messages) → ⚠️ "Version not bumped" with suggestion (patch/minor/major). Auto-fixable. - - **Changelog**: if `CHANGELOG.md`/`HISTORY.md`/`.changeset/` exists AND not touched on this branch AND changes are user-facing → ⚠️ "Changelog missing entry". Auto-fixable (draft entry). - - **Knowledge/conventions**: detect any knowledge location (`.claude/knowledge/`, `.cursor/rules/`, `AGENTS.md`, `CONVENTIONS.md`, `docs/adr/`, etc.). If new patterns detected (via commit-message heuristic from `/open` step 3f) AND knowledge location not touched → ⚠️ "Knowledge gap". Auto-fixable. - - Categorize each finding as **auto-fixable** (version, changelog, knowledge) or **manual** (README). - - Collect warnings into `DOC_WARNINGS` — surface them in the final plan (step 13) and handle user decision there. - - **Do not mutate anything here** — this is read-only inspection. +8. **Documentation readiness** (read-only at this stage): + - Run the four documentation-readiness checks defined in the shared spec at + `${CLAUDE_PLUGIN_ROOT}/docs/READINESS-CHECKS.md` (README freshness, version + bump, changelog, knowledge/conventions) — **read that file** for the + detection signals, the ✅/⚠️/❌/➖ semantics, and the auto-fixable vs. + manual classification. + - **Merge-specific behavior — read-only.** **Do not mutate anything here.** + Collect each ⚠️ into `DOC_WARNINGS`, tagged with its classification + (auto-fixable vs. manual). These surface in the final plan (step 13), + where the user's `[f]` choice triggers the actual auto-fix — keeping this + step cheap if the user later picks `[m]` to skip it. 9. **Detect merge method** (priority order): - **a) Repo allowed methods** — `gh api repos/:owner/:name --jq '{merge: .allow_merge_commit, squash: .allow_squash_merge, rebase: .allow_rebase_merge}'` diff --git a/plugins/pr-flow/skills/open/SKILL.md b/plugins/pr-flow/skills/open/SKILL.md index 8181ea0..73feebf 100644 --- a/plugins/pr-flow/skills/open/SKILL.md +++ b/plugins/pr-flow/skills/open/SKILL.md @@ -52,59 +52,23 @@ user_invocable: true ### 3b. Unpushed commits (for branch-existence check later) - Run: `git log @{u}..HEAD --oneline 2>/dev/null` — fine if it errors (upstream may not exist yet) - ### 3c. README freshness heuristic - - Run: `git diff origin/...HEAD --name-only` - - If the diff touches user-visible code (`src/`, `lib/`, `plugins/`, `skills/`, public entry points) but NOT `README.md` or `*.md` → ⚠️ "Code changed but docs untouched — README may be stale" - - If `README.md` WAS touched → ✅ - - If only internal changes (tests, configs, refactors) → ➖ "N/A" - - ### 3d. Version bump reminder (only if versioning is an established convention) - - **First detect whether this project versions releases** — do NOT warn on projects that don't. Signals: - - `package.json` has a `version` field AND git log shows prior version bumps (e.g. commits like "bump to 1.2.3", "v1.2.3", or version-field changes in recent history) - - `plugin.json` / `pyproject.toml` / `Cargo.toml` / `*.csproj` with a version field that has changed in the last ~10 commits - - Git tags following semver (`git tag --sort=-v:refname | head -5`) - - `.changeset/` directory, `release-please` config, or similar release-automation - - If **no** versioning signal → ➖ "N/A (project does not appear to version releases)" - - If versioning is used: - - Check whether any version field was bumped on this branch vs. base: - `git diff origin/...HEAD -- '**/package.json' '**/plugin.json' '**/Cargo.toml' '**/pyproject.toml'` - - If version was bumped → ✅ "Version bumped to " - - If NOT bumped AND the changes look user-facing (new feature, bug fix, breaking change — inferred from commit messages/diff) → ⚠️ "Version bump may be needed. Detected: . Suggest: " - - If NOT bumped AND changes are internal only (tests, docs, refactor) → ✅ "No bump needed (internal changes)" - - **Monorepo / multi-package awareness**: if multiple version files exist (e.g. this marketplace's `plugin.json` per plugin + root `marketplace.json`), check which one(s) are affected by the diff and remind per-package - - Respect any repo-specific semver conventions found in memory/CLAUDE.md (e.g. "patch for small changes, minor for new features") - - ### 3e. Release notes / changelog (only if the project maintains one) - - Detect presence: `CHANGELOG.md`, `CHANGELOG`, `HISTORY.md`, `RELEASES.md`, `docs/changelog/`, `.changeset/` - - If none exist → ➖ "N/A (no changelog)" - - If a changelog exists: - - Check whether it was updated on this branch: `git diff origin/...HEAD --name-only | grep -iE '(changelog|history|releases|\.changeset/)'` - - If updated → ✅ "Changelog updated" - - If NOT updated AND changes look user-facing → ⚠️ "Changelog unchanged — add an entry for this PR" - - Also suggest the section: did the version bump (2d) indicate `feat` → Added, `fix` → Fixed, `breaking` → Changed/Removed? - - If the changelog follows Keep-a-Changelog format (detect by headings), offer: "Want me to draft an entry?" - - If internal only → ✅ "No changelog entry needed (internal changes)" - - Link 2d ↔ 2e: if version bumped but changelog not, or vice versa, flag the inconsistency explicitly - - ### 3f. Project knowledge / conventions (system-agnostic) - - Detect whether the project maintains a place for conventions, rules, or learnings. Any of: - - `.claude/knowledge/`, `.claude/rules/` (gering `knowledge-system`) - - `.cursor/rules/`, `.cursorrules` (Cursor) - - `.github/copilot-instructions.md` (Copilot) - - `AGENTS.md`, `CONVENTIONS.md`, `CONTRIBUTING.md` - - `docs/adr/`, `docs/decisions/` (ADRs) - - `CLAUDE.md` with documented conventions beyond setup - - If none detected → ➖ "N/A (no knowledge/convention system detected)" - - If detected: - - Check if this branch introduces **new patterns, new conventions, or generalizable fixes** (heuristic: commit messages like "add ", "refactor to ", "fix ", or many similar files changed the same way) - - If knowledge location WAS touched → ✅ "Conventions documented" - - If nothing generalizable → ✅ "No new patterns to capture" - - If likely AND the knowledge location was NOT touched: - - **Auto-update the relevant file** — read the relevant knowledge file, draft the entry based on the branch changes, write the update - - If project uses `knowledge-system` specifically → invoke `/curate` automatically with the detected pattern as input - - Otherwise → directly edit the most relevant file (closest topical match, or the generic `AGENTS.md`/`CONVENTIONS.md` as fallback) - - Result: ✅ "Knowledge updated: `` (added: )" - - Only fall back to ⚠️ if the update genuinely requires judgment beyond mechanical doc-addition (e.g. the pattern conflicts with existing documented rules) + ### 3c–3f. Documentation readiness (README, version, changelog, knowledge) + Run the four documentation-readiness checks defined in the shared spec at + `${CLAUDE_PLUGIN_ROOT}/docs/READINESS-CHECKS.md` — **read that file** for + the detection signals, the ✅/⚠️/❌/➖ semantics, and the auto-fixable vs. + manual classification. Do not re-derive the heuristics here; the spec is + canonical. + + **Open-specific behavior — auto-resolve in place.** Per the "auto-resolve + warnings where feasible" principle (top of this skill), apply each + **auto-fixable** fix *immediately during this check phase* rather than + surfacing it as a warning: + - **Version** → bump the detected field(s) per the spec's suggestion + - **Changelog** → draft the entry under the unreleased / next-version heading + - **Knowledge** → invoke `/curate` (knowledge-system) or append to the closest knowledge file + After resolving, mark the check ✅ "". Only **manual** gaps + (README staleness) and fixes that genuinely need judgment remain as ⚠️ for + step 4. ### 3g. Tests - Detect test command (check in order):