Skip to content

Commit 9aebc55

Browse files
committed
Add test case and improve skill.md
1 parent 8c5e4ec commit 9aebc55

File tree

9 files changed

+80
-17
lines changed

9 files changed

+80
-17
lines changed

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"path": "./"
1717
},
1818
"description": "A bibliography toolkit for LaTeX",
19-
"version": "1.5.0",
19+
"version": "1.5.1",
2020
"keywords": ["bibtex", "bibliography", "latex", "overleaf", "academic", "reference", "citation"],
2121
"category": "academic",
2222
"license": "MIT"

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bibtools",
33
"description": "A bibliography toolkit for LaTeX",
4-
"version": "1.5.0",
4+
"version": "1.5.1",
55
"author": {
66
"name": "Yunguan Fu"
77
},

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "bibtools"
3-
version = "1.5.0"
3+
version = "1.5.1"
44
description = "A bibliography toolkit for LaTeX, built as agent skills"
55
requires-python = ">=3.10"
66
license = "MIT"

skills/bibtidy/SKILL.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ name: bibtidy
33
description: Use when the user wants to validate, check, or fix a BibTeX (.bib) reference file — wrong authors, stale arXiv preprints, incorrect metadata, duplicate entries, formatting issues
44
metadata:
55
short-description: Validate and fix BibTeX reference files
6-
argument-hint: <path-to-file.bib>
7-
allowed-tools: Bash(python3 *), Read, Edit, Agent, WebSearch
6+
allowed-tools: Bash(python3 *), Bash(cp *), Bash(rm *), Read, Agent, WebSearch
87
---
98

109
Use this skill whenever the user wants to validate, check, clean up, or fix a BibTeX `.bib` file.
1110

12-
If invoked with an argument, operate on this file: `$ARGUMENTS`.
11+
If the user request or skill invocation includes an explicit `.bib` path, operate on that file.
1312

14-
If the file path is missing or does not exist, ask for the path. In Claude Code slash-command usage, the fallback is:
15-
"Usage: /bibtidy <path-to-file.bib>"
13+
If the file path is missing or does not exist, ask for the path.
14+
15+
Usage examples:
16+
- Claude Code slash command: `/bibtidy <path-to-file.bib>`
17+
- Codex request: `Use the bibtidy skill to validate and fix <path-to-file.bib>`
1618

1719
You are a meticulous academic reference checker. Process the .bib file entry by entry, verifying each against external sources and fixing errors in-place.
1820

@@ -48,7 +50,7 @@ Use `$TOOLS_DIR` in every invocation.
4850

4951
## Output Format for Changed Entries
5052

51-
Each targeted edit MUST contain the original entry, one or more source URLs, an explanation, and the corrected entry. Include URLs for all sources used (CrossRef, DOI, venue page), each on its own `% bibtidy:` line.
53+
For `fix` patches, each targeted edit MUST contain the original entry, one or more source URLs, an explanation, and the corrected entry. Include URLs for all sources used (CrossRef, DOI, venue page), each on its own `% bibtidy:` line.
5254

5355
```
5456
% @<type>{<key>,
@@ -70,6 +72,10 @@ Each targeted edit MUST contain the original entry, one or more source URLs, an
7072
- **Part 3**`% bibtidy: ` followed by explanation of what changed.
7173
- **Part 4** — corrected entry.
7274

75+
Exceptions:
76+
- `not_found` entries get the `% bibtidy: NOT FOUND ...` line plus the fully commented-out original entry. Do NOT add a URL line.
77+
- `duplicate` entries get `% bibtidy: DUPLICATE of <other_key> — consider removing` above the original entry. Do NOT add URL or explanation lines unless the tool behavior changes.
78+
7379
For unchanged entries, do NOT add any comments or URLs.
7480

7581
## Workflow
@@ -79,16 +85,16 @@ For unchanged entries, do NOT add any comments or URLs.
7985
3. Preserve `@string`, `@preamble`, `@comment` blocks verbatim
8086
4. Run duplicate detection: `python3 $TOOLS_DIR/duplicates.py <file.bib>`
8187
5. **Run field comparison**: `python3 $TOOLS_DIR/compare.py <file.bib>` — this programmatically compares every entry against CrossRef and returns exact field-level mismatches. Do NOT skip this step or rely on visual comparison alone. The output is a JSON list; each element has `key`, `versions` (a list of CrossRef matches, each with `mismatches`, `url`, `doi`, etc.), and `error`. **Skip rule**: if an entry has zero mismatches across all versions and no error in the compare.py output, skip it entirely — do NOT investigate, modify, or add comments to it. Only proceed with entries that compare.py flagged (mismatches, errors, or duplicates from step 4).
82-
6. **Verify every planned modification with web search** — for entries that compare.py flagged with mismatches or errors, and for entries flagged as duplicates, gather a source URL and double-check the modification via web search. Entries where `compare.py` returned an error (e.g. "No exact title match") still need full verification — the subagent should search for the paper and check all fields. **Important: subagents MUST NOT override `compare.py` field values.** CrossRef is the authoritative source for metadata (pages, volume, number, etc.) because it receives data directly from publishers via DOI registration. When web search finds a conflicting value (e.g. different page numbers on a conference website), always use the CrossRef value and add `% bibtidy: REVIEW` if desired — but do NOT keep the old value.
88+
6. **Verify every planned modification with web search** — for entries that compare.py flagged with mismatches or errors, and for entries flagged as duplicates, verify the planned action via web search. For `fix` patches, gather one or more source URLs. Entries where `compare.py` returned an error (e.g. "No exact title match") still need full verification — the verification agent should search for the paper and check all fields. **Important: verification agents MUST NOT override `compare.py` field values.** CrossRef is the authoritative source for metadata (pages, volume, number, etc.) because it receives data directly from publishers via DOI registration. When web search finds a conflicting value (e.g. different page numbers on a conference website), always use the CrossRef value and add `% bibtidy: REVIEW` if desired — but do NOT keep the old value.
8389
7. **Flag hallucinated/non-existent references** — if compare.py returned an error (e.g. "No CrossRef results found" or "No exact title match in CrossRef results") AND web search also finds no matching paper, the reference likely does not exist. Add `% bibtidy: NOT FOUND — no matching paper on CrossRef or web search; verify this reference exists` above the entry, then comment out the entire entry (prefix every line with `% `). Do NOT add a URL line.
84-
8. Apply fixes **sequentially** with targeted edits — do NOT rewrite the entire file. You MUST apply **every** mismatch reported by `compare.py` — do not skip any field (including `number`, `pages`, `volume`). Use the `crossref_value` exactly as given (do NOT rephrase, reformat, or partially apply it). For title mismatches on preprint→published upgrades, replace the entire title with the CrossRef title — do NOT try to edit parts of the old title. Never reject a CrossRef value because another source disagrees. **URL rule**: for every changed entry, include `% bibtidy: <url>` lines so the user can verify the correction. Include the CrossRef URL from compare.py's `url` field when available, plus any other authoritative source (DOI URL, venue page) found via web search.
90+
8. Apply fixes **sequentially** using `edit.py` — do NOT edit the .bib file directly with agent editing tools (for example, Claude Code Edit or Codex `apply_patch`), and do NOT rewrite the entire file. Build a patches.json for each entry (or batch) and run `python3 $TOOLS_DIR/edit.py <file.bib> <patches.json>`. This ensures the commented original, source URLs, and explanation are always included. You MUST apply **every** mismatch reported by `compare.py` — do not skip any field (including `number`, `pages`, `volume`). Use the `crossref_value` exactly as given (do NOT rephrase, reformat, or partially apply it). For title mismatches on preprint→published upgrades, replace the entire title with the CrossRef title — do NOT try to edit parts of the old title. Never reject a CrossRef value because another source disagrees. Every patch MUST include `urls` (list of source URLs) and `explanation` (what changed and why). Include the CrossRef URL from compare.py's `url` field when available, plus any other authoritative source (DOI URL, venue page) found via web search.
8591
9. Run format validation; fix violations and re-run until clean
8692
10. Delete backup: `rm <file>.bib.orig`
8793
11. Print a Markdown summary table with headers `Metric | Count` and exactly these rows: total entries, verified, fixed, not found. Do NOT include a separate "needs manual review" row.
8894

8995
## Parallel Verification with Subagents
9096

91-
Use subagents, when available, to verify multiple entries concurrently. This dramatically reduces wall-clock time (e.g., 7 entries: ~1 min parallel vs ~5 min sequential; 100 entries: ~3 min vs ~40 min).
97+
Use subagents, when available, to verify multiple entries concurrently. This dramatically reduces wall-clock time (e.g., 7 entries: ~1 min parallel vs ~5 min sequential; 100 entries: ~3 min vs ~40 min). If subagents are unavailable, do the same verification work sequentially yourself.
9298

9399
**Step 1 — Dispatch verification agents:** For entries that `compare.py` flagged with mismatches or errors, and any duplicate entries you plan to annotate, launch a subagent that:
94100
- For mismatches: uses web search to confirm the CrossRef data (especially for preprint upgrades and author changes)
@@ -97,11 +103,11 @@ Use subagents, when available, to verify multiple entries concurrently. This dra
97103

98104
**When CrossRef fails**, find the paper's official venue page via web search. Many venues (JMLR, NeurIPS, CVPR, etc.) provide a downloadable `.bib` file — fetch it directly when possible. An official `.bib` is the most reliable source: it has exact title, authors, volume, number, and pages with no guessing.
99105

100-
Launch all agents in a single message so they run concurrently. Group into batches of ~10 if there are many entries.
106+
Launch verification subagents in one batch so they run concurrently. Group into batches of ~10 if there are many entries.
101107

102108
**Step 2 — Collect results:** Read each agent's returned summary.
103109

104-
**Step 3 — Apply edits sequentially:** Using the lookup results, apply one targeted edit at a time. Edits MUST be sequential (parallel edits to the same file cause conflicts).
110+
**Step 3 — Apply edits sequentially using `edit.py`:** Using the lookup results, build a patches.json and run `python3 $TOOLS_DIR/edit.py <file.bib> <patches.json>` one entry at a time. Do NOT edit the .bib file directly with agent editing tools such as Claude Code Edit or Codex `apply_patch`. Edits MUST be sequential (parallel edits to the same file cause conflicts).
105111

106112
**Example agent prompt:**
107113
```
@@ -160,13 +166,14 @@ For each `@article`, `@inproceedings`, `@book`, etc.:
160166
| Missing `% bibtidy:` URL | Every changed entry needs a URL — use DOI URL or venue page |
161167
| Incomplete commented original | Comment out ALL lines of the original, including closing `}` |
162168
| Adding comments to unchanged entries | Only changed entries get bibtidy comments — if compare.py reports zero mismatches and no error, do not touch the entry |
163-
| Rewriting entire file | Use one targeted edit per entry |
169+
| Rewriting entire file | Use `edit.py` with one entry or a small batch at a time; never rewrite the `.bib` file directly |
164170
| Deleting duplicate entries | Flag with comment only — never delete |
165171
| Losing `@string`/`@preamble` blocks | Preserve verbatim, don't touch |
166172
| Single hyphen in page ranges | Always use `--` (double hyphen) for BibTeX page ranges |
167173
| Partially applying title changes | When CrossRef title differs (e.g. preprint→published), replace the ENTIRE title with the CrossRef value — do not edit substrings |
168174
| Ignoring `number` field mismatches | `compare.py` reports `number` mismatches — apply them |
169175
| Adding `doi` when entry didn't have one | Never inject a `doi` field into an entry that lacks one |
176+
| Using agent editing tools instead of `edit.py` | Always use `edit.py` to apply changes, never edit the .bib file directly with Claude Code Edit, Codex `apply_patch`, or similar tools. Direct edits skip the commented original, URLs, and explanation |
170177

171178
## Preserve
172179

tests/bibtidy/fixtures/expected.bib

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,26 @@ @article{khader2022medical
153153
year={2023}
154154
}
155155

156+
% Author list uses "and others" instead of full list
157+
% @inproceedings{kirillov2023segment,
158+
% title={Segment anything},
159+
% author={Kirillov, Alexander and Mintun, Eric and Ravi, Nikhila and Mao, Hanzi and Rolland, Chloe and Gustafson, Laura and Xiao, Tete and Whitehead, Spencer and Berg, Alexander C and Lo, Wan-Yen and others},
160+
% booktitle={Proceedings of the IEEE/CVF International Conference on Computer Vision},
161+
% pages={3992--4003},
162+
% year={2023},
163+
% doi={10.1109/ICCV51070.2023.00371}
164+
% }
165+
% bibtidy: https://doi.org/10.1109/ICCV51070.2023.00371
166+
% bibtidy: expanded author list from "and others" to full list
167+
@inproceedings{kirillov2023segment,
168+
title={Segment anything},
169+
author={Kirillov, Alexander and Mintun, Eric and Ravi, Nikhila and Mao, Hanzi and Rolland, Chloe and Gustafson, Laura and Xiao, Tete and Whitehead, Spencer and Berg, Alexander C and Lo, Wan-Yen and Doll{\'a}r, Piotr and Girshick, Ross},
170+
booktitle={Proceedings of the IEEE/CVF International Conference on Computer Vision},
171+
pages={3992--4003},
172+
year={2023},
173+
doi={10.1109/ICCV51070.2023.00371}
174+
}
175+
156176
% Published article should not be downgraded to preprint
157177
@article{tzou2022coronavirus,
158178
title={Coronavirus Resistance Database (CoV-RDB): SARS-CoV-2 susceptibility to monoclonal antibodies, convalescent plasma, and plasma from vaccinated persons},

tests/bibtidy/fixtures/input.bib

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ @article{khader2022medical
9696
year={2022}
9797
}
9898

99+
% Author list uses "and others" instead of full list
100+
@inproceedings{kirillov2023segment,
101+
title={Segment anything},
102+
author={Kirillov, Alexander and Mintun, Eric and Ravi, Nikhila and Mao, Hanzi and Rolland, Chloe and Gustafson, Laura and Xiao, Tete and Whitehead, Spencer and Berg, Alexander C and Lo, Wan-Yen and others},
103+
booktitle={Proceedings of the IEEE/CVF International Conference on Computer Vision},
104+
pages={3992--4003},
105+
year={2023},
106+
doi={10.1109/ICCV51070.2023.00371}
107+
}
108+
99109
% Published article should not be downgraded to preprint
100110
@article{tzou2022coronavirus,
101111
title={Coronavirus Resistance Database (CoV-RDB): SARS-CoV-2 susceptibility to monoclonal antibodies, convalescent plasma, and plasma from vaccinated persons},

tests/bibtidy/test_validate.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ def test_parenthesized_entry_rejected(self):
164164
"volume": (None, "13"),
165165
"year": ("2022", "2023"),
166166
},
167+
"kirillov2023segment": {
168+
"author": (
169+
"Kirillov, Alexander and Mintun, Eric and Ravi, Nikhila and Mao, Hanzi"
170+
" and Rolland, Chloe and Gustafson, Laura and Xiao, Tete and Whitehead, Spencer"
171+
" and Berg, Alexander C and Lo, Wan-Yen and others",
172+
"Kirillov, Alexander and Mintun, Eric and Ravi, Nikhila and Mao, Hanzi"
173+
" and Rolland, Chloe and Gustafson, Laura and Xiao, Tete and Whitehead, Spencer"
174+
" and Berg, Alexander C and Lo, Wan-Yen and Doll{\\'a}r, Piotr and Girshick, Ross",
175+
),
176+
},
167177
"tzou2022coronavirus": {},
168178
"aichberger2025semantically": {
169179
"title": (

tests/bibtidy/validate.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,21 @@ def check_hallucinated_metadata_fixed(text):
293293
return t
294294

295295

296+
def check_author_expansion(text):
297+
"""Author list with 'and others' should be expanded with commented original + source."""
298+
t = TestResult("Author list expanded (kirillov2023segment)")
299+
entry = find_entry_block(text, "kirillov2023segment")
300+
t.check(entry is not None, "Entry still exists")
301+
t.check(find_commented_entry(text, "kirillov2023segment"), "Original entry commented out")
302+
t.check(has_url(text, "kirillov2023segment"), "URL provided")
303+
if entry:
304+
author = get_field(entry, "author") or ""
305+
t.check("and others" not in author, "'and others' removed")
306+
t.check("Kirillov" in author, "First author preserved")
307+
t.check(author.count(" and ") > 10, "Full author list present (>10 'and' separators)")
308+
return t
309+
310+
296311
def check_published_article_not_downgraded(text):
297312
"""Published article should not be downgraded to a preprint."""
298313
t = TestResult("Published article not downgraded (tzou2022coronavirus)")
@@ -315,7 +330,7 @@ def test_entry_count(text):
315330
cleaned = remove_special_blocks(text)
316331
cleaned = re.sub(r"(?m)^[ \t]*%.*$", "", cleaned)
317332
all_at = re.findall(r"^[ \t]*@(\w+)\{", cleaned, re.MULTILINE)
318-
t.check(len(all_at) == 10, f"Expected 10 active entries, found {len(all_at)}")
333+
t.check(len(all_at) == 11, f"Expected 11 active entries, found {len(all_at)}")
319334
return t
320335

321336

@@ -350,6 +365,7 @@ def main():
350365
check_wrong_pages_fixed(text),
351366
check_title_change_upgraded(text),
352367
check_hallucinated_metadata_fixed(text),
368+
check_author_expansion(text),
353369
check_published_article_not_downgraded(text),
354370
check_hallucinated_entry_flagged(text),
355371
]

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)