Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e2e8ed7
cli/sync(feat[update_repo]) Detect and report errored git syncs
tony Feb 7, 2026
5875696
tests(fix[test_cli]) Skip errored sync tests when SyncResult unavailable
tony Feb 7, 2026
87e6e9e
pyproject(uv.sources) Point libvcs at errored-sync-behavior branch
tony Feb 7, 2026
26e3f1a
cli/sync,tests(feat[update_repo]) Fix set_remotes for non-git VCS, ad…
tony Feb 7, 2026
f09079c
tests(fix[test_sync_rev_branch_mismatch]) Remove xfail, update libvcs…
tony Feb 7, 2026
dc67144
tests(feat[test_cli]) Add summary-line, all-fail, and cross-VCS sync …
tony Feb 7, 2026
cc4b47c
tests(feat[test_cli]) Add xfail tests for phantom None message and am…
tony Feb 7, 2026
e9722c9
cli/sync(fix[sync]) Show actual search term instead of "None" for pat…
tony Feb 7, 2026
284e6bd
tests(fix[test_cli]) Remove xfail from test_sync_ambiguous_branch_dir…
tony Feb 7, 2026
81d9692
tests(feat[test_cli]) Add xfail test for unmatched patterns in sync s…
tony Feb 7, 2026
5387c2c
cli/sync(fix[sync]) Count unmatched patterns as failures in sync summary
tony Feb 7, 2026
2031bc2
tests(fix[test_cli]) Update ambiguous branch test as regression test
tony Feb 7, 2026
248c2a4
commands(feat[tdd-fix]) Add TDD bug-fix workflow skill
tony Feb 7, 2026
4ada430
tests(feat[test_cli]) Add xfail test for duplicate unmatched pattern …
tony Feb 7, 2026
40ad2e9
cli/sync(fix[sync]) Downgrade duplicate user-facing log.info to log.d…
tony Feb 7, 2026
6f7c6cd
tests(fix[test_cli]) Remove xfail from duplicate log test
tony Feb 7, 2026
f035455
pyproject(fix[uv.lock]) Update libvcs lock to include refs/heads disa…
tony Feb 7, 2026
c7308fb
tests(fix[test_sync]) Create non-empty repos in sync fixtures
tony Feb 7, 2026
ba3311b
cli/sync(fix[sync]) Emit summary when all sync patterns are unmatched
tony Feb 7, 2026
2e08063
cli/sync(refactor[sync]) Extract _emit_summary helper to deduplicate …
tony Feb 7, 2026
ddea149
py(deps) libvcs 0.38.6 -> 0.39.0
tony Feb 7, 2026
999bc42
docs(CHANGES) Note libvcs v0.39.0 bump
tony Feb 7, 2026
a7b05d3
commands(feat[changelog]) Add /changelog command for CHANGES entry ge…
tony Feb 7, 2026
da19fcb
commands(feat[review-feedback]) Add /review-feedback command for exte…
tony Feb 7, 2026
777f83e
cli/sync(fix[summary]) Separate unmatched patterns from repo total in…
tony Feb 7, 2026
93da74d
tests(fix[test_sync]) Assert exit code and check stderr expectations
tony Feb 7, 2026
5f73f37
cli/sync(fix[SyncFailedError]) Include repo identity in sync error me…
tony Feb 7, 2026
b8b27a6
cli/sync(fix[summary_only]) Suppress unmatched-pattern lines in --sum…
tony Feb 7, 2026
7a5b40c
cli/sync(fix[summary]) Omit "0 previewed" from non-dry-run sync summary
tony Feb 7, 2026
8bb7ba5
tests(refactor[test_cli]) Use write_config helper for config file cre…
tony Feb 7, 2026
27d6f44
cli/sync(feat[exit-on-error]) Exit non-zero on unmatched patterns wit…
tony Feb 7, 2026
d83133d
docs(fix[tdd-fix.md]) Replace Unicode em-dashes with ASCII double hyp…
tony Feb 7, 2026
1b1333a
cli/sync(fix[_emit_summary]) Color the total count in sync summary ou…
tony Feb 7, 2026
382213c
tests(refactor[test_cli]) Use libvcs factory fixtures for SVN/HG erro…
tony Feb 7, 2026
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
213 changes: 213 additions & 0 deletions .claude/commands/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
description: Generate CHANGES entries from branch commits and PR context
argument-hint: "[optional additional context about the changes]"
allowed-tools: Bash(git log:*), Bash(git branch:*), Bash(git symbolic-ref:*), Bash(gh pr view:*), Bash(gh pr list:*), Read, Grep, Glob, Edit
---

# Changelog Entry Generator

Generate well-formatted CHANGES entries from the current branch's commits and PR context. This command analyzes commits, categorizes them, and inserts entries into the CHANGES file after user review.

Additional context from user: $ARGUMENTS

---

## Phase 1: Gather Context

**Goal**: Collect all information needed to generate changelog entries.

**Actions**:

1. **Detect project name** from `pyproject.toml`:
- Read `pyproject.toml` and extract the `name` field under `[project]`
- This is used for matching the CHANGES heading format (`## <project> vX.Y.Z`)

2. **Detect trunk branch**:
```
git symbolic-ref refs/remotes/origin/HEAD
```
- Strip `refs/remotes/origin/` prefix to get branch name
- Fall back to `master` if the above fails

3. **Verify not on trunk**:
- Check current branch: `git branch --show-current`
- If on trunk, report "Already on trunk branch — nothing to generate" and stop

4. **Read CHANGES file**:
- Find the CHANGES file (usually `CHANGES` with no extension at project root)
- Identify the unreleased section heading (e.g., `## vcspull v1.51.x (unreleased)`)
- Locate the `<!-- END PLACEHOLDER` marker line — this is where new entries are inserted
- Note any existing entries between `<!-- END PLACEHOLDER -->` and the next `## ` release heading
- Record which section headings (e.g., `### Bug fixes`, `### Features`) already exist in the unreleased block

5. **Check for PR**:
```
gh pr view --json number,title,body,labels 2>/dev/null
```
- If a PR exists, extract the number, title, body, and labels
- If no PR exists, note that `(#???)` placeholders will be used

6. **Get commits**:
```
git log <trunk>..HEAD --oneline
```
- Also get full commit details for body parsing:
```
git log <trunk>..HEAD --format='%H %s%n%b---'
```
- If no commits ahead of trunk, report "No commits ahead of trunk" and stop

---

## Phase 2: Categorize Commits

**Goal**: Parse commits into changelog categories and group related ones.

### Commit type mapping

Parse the commit type from the `Component(type[sub])` convention in commit subjects:

| Commit type | CHANGES section | Notes |
|---|---|---|
| `feat` | Features / New features | New functionality |
| `fix` | Bug fixes | Bug fixes |
| `docs` | Documentation | Doc changes |
| `test` | Tests | Test additions/changes |
| `refactor` | (only if user-visible) | Skip internal-only refactors |
| `chore`, `deps` | Development | Maintenance, dependency bumps |
| `style` | (skip) | Formatting-only changes |

### Grouping rules

- **TDD workflow sequences**: An xfail commit + a fix commit + an xfail-removal commit should collapse into a **single** bug fix entry. The fix commit's message is the primary source.
- **Dependency bumps**: A `pyproject` deps commit + a CHANGES doc commit = 1 entry under "Breaking changes" (if it's a minimum version bump) or "Development"
- **Multi-commit features**: Sequential `feat` commits on the same component collapse into one entry
- **Skip entirely**: merge commits, `commands(feat[...])` commits (adding claude commands), lock-only changes, internal-only refactors

### Output of this phase

A structured list of entries grouped by section, each with:
- Section name (e.g., "Bug fixes")
- Entry text (formatted markdown)
- Source commit(s) for traceability

---

## Phase 3: Generate Entries

**Goal**: Write the exact markdown to be inserted into CHANGES.

### Format rules (derived from existing CHANGES files)

1. **Section headings**: Use `### Section Name` (e.g., `### Bug fixes`, `### Features`)

2. **Section order** (only include sections that have entries):
- Breaking changes
- Features / New features
- Bug fixes
- Documentation
- Tests
- Development

3. **Simple entries** — single bullet:
```markdown
- Brief description of the change (#123)
```

4. **Detailed entries** — sub-heading with description:
```markdown
#### Component: Brief description (#123)

Explanatory paragraph about what changed and why.

- Bullet point with specific detail
- Another detail
```

5. **PR references**:
- If PR number is known: `(#512)`
- If no PR exists: `(#???)`

6. **Match existing style**:
- Check whether the project uses "Bug fixes" or "Bug Fixes" (match existing capitalization)
- Check whether "Features" or "New features" is used
- Preserve the project's conventions

### Entry writing guidelines

- Write from the user's perspective — what changed for them, not internal implementation details
- Lead with the *what*, not the *why* (the description paragraph handles *why*)
- Use present tense for the entry title ("Add support for..." not "Added support for...")
- Don't repeat the section heading in the entry text
- Keep bullet entries to 1-2 lines; use the sub-heading format for anything needing more explanation

---

## Phase 4: Present for Review

**CRITICAL**: This is a mandatory confirmation gate. Never skip to Phase 5 without explicit user approval.

**Present to the user**:

1. **Summary line**:
```
Branch: <branch-name> | Commits: <count> | PR: #<number> (or "none")
```

2. **Proposed entries** in a fenced code block showing the exact markdown:
````
```markdown
### Bug fixes

- Fix phantom "None" message when syncing path-based patterns (#512)

#### cli/sync: Report errored git syncs in summary (#512)

`update_repo()` now detects and reports git sync failures instead of
silently succeeding. The sync summary shows errored repositories
alongside successful and failed counts.
```
````

3. **Insertion point**: Describe where these entries will go:
```
Insert after: <!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
Before: (next release heading or existing unreleased entries)
```

4. **Ask the user**: "Insert these entries into CHANGES? You can also ask me to modify them first."

**Wait for user response.** Do not proceed until they confirm.

---

## Phase 5: Insert into CHANGES

**Goal**: Insert the approved entries into the CHANGES file.

**Only execute after explicit user approval in Phase 4.**

### Insertion logic

1. **Find the insertion point**: Locate the `<!-- END PLACEHOLDER` line in the CHANGES file

2. **Check for existing unreleased section headings**:
- If the CHANGES file already has a `### Bug fixes` section in the unreleased block, and the new entries also have bug fixes, **append** to the existing section rather than creating a duplicate heading
- If the section doesn't exist yet, insert the full section with heading

3. **Insert the entries**:
- Use the Edit tool to insert after the `<!-- END PLACEHOLDER -->` line
- Ensure exactly one blank line between the placeholder comment and the first section heading
- Ensure exactly one blank line between sections

4. **Show the result**:
- After editing, read the modified region of the CHANGES file and display it so the user can verify
- Note: this command does NOT commit — the user decides when to stage and commit the CHANGES update

### Edge case: merging with existing entries

If there are already entries below the placeholder in the unreleased section:

- New entries for **existing sections** are appended at the end of that section (before the next `###` heading or the next `## ` release heading)
- New entries for **new sections** follow the section order defined in Phase 3 — insert the new section in the correct position relative to existing sections
- Never duplicate a `###` section heading
185 changes: 185 additions & 0 deletions .claude/commands/review-feedback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
description: Process external code review feedback — validate each point, apply valid fixes as separate commits
argument-hint: "Paste the external code review feedback here"
allowed-tools: Bash(git diff:*), Bash(git log:*), Bash(git branch:*), Bash(git status:*), Bash(git add:*), Bash(git commit:*), Bash(uv run ruff:*), Bash(uv run mypy:*), Bash(uv run py.test:*), Bash(uv run pytest:*), Read, Grep, Glob, Edit, Write
---

# External Code Review Feedback Processor

Process external code review feedback against the current branch. Validate each feedback point independently, apply valid fixes as separate atomic commits, and ensure all quality gates pass after each commit.

Review feedback to process: $ARGUMENTS

---

## Phase 1: Gather Context

**Goal**: Understand what changed on this branch and parse the review feedback.

**Actions**:

1. **Get the diff** against the trunk branch:
```
git diff origin/master --stat
git diff origin/master
```

2. **Get commit history** for this branch:
```
git log origin/master..HEAD --oneline
```

3. **Read modified files** identified in the diff to understand the full context of each change

4. **Parse the review feedback** into a numbered list of discrete, actionable points. Each point should capture:
- **What**: The specific issue or suggestion
- **Where**: File and approximate location
- **Category**: bug, style, logic, naming, performance, test gap, documentation, etc.

5. **Create a todo list** tracking each feedback point with its validation status

---

## Phase 2: Validate Each Feedback Point

**Goal**: Independently assess whether each feedback point is valid and actionable.

For EACH feedback point:

1. **Read the relevant code** — the exact lines the reviewer is referring to

2. **Assess validity** using these criteria:
- **Valid**: The feedback identifies a real issue or improvement that aligns with project conventions (CLAUDE.md)
- **Already addressed**: The issue was already fixed in a later commit on the branch
- **Incorrect**: The reviewer misread the code or the suggestion would introduce a bug
- **Out of scope**: Valid concern but belongs in a separate PR/issue
- **Subjective/style**: Preference-based with no clear project convention favoring it

3. **Document the verdict** for each point:
- If valid: note exactly what change is needed and in which file(s)
- If invalid: note the specific reason (cite code, tests, or CLAUDE.md conventions)

4. **Present the validation results** to the user before making any changes:
- List each feedback point with its verdict (valid / invalid / out of scope)
- For invalid points, explain why concisely
- For valid points, describe the planned fix
- **Wait for user confirmation** before proceeding to Phase 3

---

## Phase 3: Apply Valid Fixes (One Commit Per Point)

**Goal**: Apply each valid feedback point as a separate, atomic commit.

**CRITICAL**: Process one feedback point at a time. Complete the full cycle for each before moving to the next.

For EACH valid feedback point:

### Step 1: Apply the Fix

- Make the minimal change that addresses the feedback
- Do not bundle unrelated changes
- Follow project conventions from CLAUDE.md:
- `from __future__ import annotations` at top of files
- `import typing as t` namespace style
- NumPy docstring style
- Functional tests only (no test classes)

### Step 2: Run Quality Gates

Run ALL quality gates and ensure they pass:

```bash
uv run ruff check . --fix --show-fixes
uv run ruff format .
uv run mypy
uv run py.test --reruns 0 -vvv
```

- If any gate fails, fix the issue before proceeding
- If a test fails due to the change, either:
- Adjust the fix to be correct, OR
- Update the test if the reviewer's feedback changes expected behavior
- ALL FOUR gates must pass before committing

### Step 3: Commit

Stage only the files changed for this specific feedback point:

```bash
git add <specific-files>
```

Use the project commit message format with HEREDOC:

```bash
git commit -m "$(cat <<'EOF'
Component(fix[subcomponent]) Brief description of the review feedback fix

why: Address code review feedback — <what the reviewer pointed out>
what:
- <specific change 1>
- <specific change 2>
EOF
)"
```

**Commit type guidance**:
- `fix` for bug fixes or correctness issues
- `refactor` for code clarity or structure improvements
- `style` for naming or formatting changes
- `docs` for documentation or docstring fixes
- `test` for test improvements

### Step 4: Verify Clean State

After committing, confirm:
```bash
git status
git diff
```

No uncommitted changes should remain before moving to the next feedback point.

---

## Phase 4: Summary

After processing all valid points, present a summary:

1. **Applied fixes**: List each committed fix with its commit hash
2. **Skipped points**: List each invalid/out-of-scope point with the reason
3. **Final verification**: Run the full quality gate one last time:
```bash
uv run ruff check . --fix --show-fixes
uv run ruff format .
uv run mypy
uv run py.test --reruns 0 -vvv
```
4. Report the final pass/fail status

---

## Recovery: Quality Gate Failure

If quality gates fail after applying a fix:

1. **Identify** which gate failed and why
2. **Fix** the issue (adjust the change, not bypass the gate)
3. **Re-run** all four gates
4. If the fix cannot be made to pass all gates after 2 attempts:
- Revert the change: `git checkout -- <files>`
- Mark the feedback point as "valid but could not apply cleanly"
- Move to the next point
- Report the issue in the Phase 4 summary

---

## Rules

- Never skip quality gates
- Never bundle multiple feedback points into one commit
- Never modify code that isn't related to the feedback being addressed
- Always wait for user confirmation after Phase 2 validation
- Always use project commit message conventions
- If a feedback point requires changes in multiple files, that is still ONE commit (one logical change)
Loading