Skip to content

Commit d4a5ebc

Browse files
rgambeegithub-actions[bot]
authored andcommitted
Write Claude skill for bumping SDK version (#5143)
Sourced from commit 910a53f71ba8d8117f5523700ee54170eebe2222
1 parent 7c757cc commit d4a5ebc

3 files changed

Lines changed: 130 additions & 75 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
name: bump-sdk-version
3+
description: Bump the FutureSearch SDK version across all files. Use when releasing a new SDK version, updating version numbers, or the user says bump version, release, version bump.
4+
---
5+
6+
# Bump SDK Version
7+
8+
## Versioning Guidelines
9+
10+
We use **semantic versioning** (MAJOR.MINOR.PATCH) while at major version 0:
11+
12+
| Bump | When | Examples |
13+
|:-----|:-----|:---------|
14+
| **Patch** (0.8.0 -> 0.8.1) | Bug fixes, docs changes, small tweaks, dependency updates | Fix a typo in output, update a dependency |
15+
| **Minor** (0.8.1 -> 0.9.0) | New features, new operation types, API additions | Add a new operation, new SDK method, new MCP tool |
16+
| **Patch for breaking changes** that are trivial to adapt | Rename a parameter, change a default | Rename `max_rows` to `row_limit` |
17+
| **Minor for breaking changes** that require migration | Remove or restructure API surface | Remove an operation type, change return format |
18+
19+
If unsure, ask the user which component to bump.
20+
21+
## Files to Update
22+
23+
All paths are relative to `futuresearch-python/`. Update the version string in each:
24+
25+
1. **`pyproject.toml`**`project.version` (the source of truth)
26+
2. **`.claude-plugin/plugin.json`**`version`
27+
3. **`.claude-plugin/marketplace.json`**`plugins[0].version`
28+
4. **`gemini-extension.json`**`version`
29+
5. **`futuresearch-mcp/pyproject.toml`**`project.version` AND `dependencies` (`futuresearch>=X.Y.Z`)
30+
6. **`futuresearch-mcp/server.json`**`version` AND `packages[0].version`
31+
7. **`futuresearch-mcp/manifest.json`**`version`
32+
8. **`CITATION.cff`**`version` AND `date-released` (set to today's date)
33+
9. **`README.md`** — BibTeX `version` field in the citation block
34+
10. **`stubs/everyrow/pyproject.toml`**`project.version` AND `dependencies` (`futuresearch>=X.Y.Z`)
35+
11. **`stubs/everyrow-mcp/pyproject.toml`**`project.version` AND `dependencies` (`futuresearch-mcp>=X.Y.Z`)
36+
37+
After editing, regenerate the lock file:
38+
39+
```bash
40+
cd futuresearch-python && uv lock
41+
```
42+
43+
## Verification Steps
44+
45+
### 1. Run version consistency tests
46+
47+
```bash
48+
cd futuresearch-python && uv run pytest tests/test_version.py -v
49+
```
50+
51+
### 2. Search for stale version references
52+
53+
Search for the OLD version number to catch any files that were missed. Use `--hidden` (or equivalent) to include `.claude-plugin/` and other dotfiles:
54+
55+
```bash
56+
cd futuresearch-python && rg --hidden "OLD_VERSION" --glob '!.venv' --glob '!uv.lock' --glob '!*.pyc' --glob '!__pycache__'
57+
```
58+
59+
Be sure to escape the `.` characters in the version number: `"1\.2\.3"`.
60+
61+
Review any hits — some may be false positives: e.g., changelogs, migration notes, 3rd party packages.
62+
63+
### 3. Commit
64+
65+
Create a branch and commit with message: `Bump SDK version to X.Y.Z`
66+
67+
## Important: Dedicated PR
68+
69+
Version bumps should be in their own PR — do not bundle them with feature or fix changes. The commit message and PR title should be: `Bump SDK version to X.Y.Z`
70+
71+
## Checklist
72+
73+
- [ ] All 11 files updated with new version
74+
- [ ] `CITATION.cff` date-released set to today
75+
- [ ] Dependency version floors updated (`futuresearch>=X.Y.Z`, `futuresearch-mcp>=X.Y.Z`)
76+
- [ ] `uv lock` regenerated
77+
- [ ] `uv run pytest tests/test_version.py` passes
78+
- [ ] `rg --hidden "OLD_VERSION"` shows no unexpected hits
79+
- [ ] PR contains only version bump changes (no other features or fixes)

.github/workflows/skill-version-check.yaml

Lines changed: 0 additions & 27 deletions
This file was deleted.

tests/test_version.py

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import re
23
import tomllib
34

45
import httpx
@@ -9,68 +10,70 @@
910

1011

1112
def test_version_consistency(pytestconfig: pytest.Config):
12-
"""Check that version is consistent across pyproject.toml, plugin.json, gemini-extension.json, marketplace.json, futuresearch-mcp/pyproject.toml, and futuresearch-mcp/server.json."""
13+
"""Check that version is consistent across all files that contain the SDK version."""
1314
root = pytestconfig.rootpath
1415

1516
pyproject_path = root / "pyproject.toml"
1617
with open(pyproject_path, "rb") as f:
1718
pyproject = tomllib.load(f)
1819
pyproject_version = pyproject["project"]["version"]
1920

20-
plugin_json_path = root / ".claude-plugin" / "plugin.json"
21-
with open(plugin_json_path) as f:
22-
plugin_json = json.load(f)
23-
plugin_version = plugin_json["version"]
21+
# Collect all version sources with labels
22+
sources: dict[str, str] = {}
2423

25-
gemini_json_path = root / "gemini-extension.json"
26-
with open(gemini_json_path) as f:
27-
gemini_json = json.load(f)
28-
gemini_version = gemini_json["version"]
24+
with open(root / ".claude-plugin" / "plugin.json") as f:
25+
sources[".claude-plugin/plugin.json version"] = json.load(f)["version"]
2926

30-
marketplace_json_path = root / ".claude-plugin" / "marketplace.json"
31-
with open(marketplace_json_path) as f:
32-
marketplace_json = json.load(f)
33-
marketplace_version = marketplace_json["plugins"][0]["version"]
27+
with open(root / ".claude-plugin" / "marketplace.json") as f:
28+
sources[".claude-plugin/marketplace.json plugins[0].version"] = json.load(f)[
29+
"plugins"
30+
][0]["version"]
3431

35-
mcp_pyproject_path = root / "futuresearch-mcp" / "pyproject.toml"
36-
with open(mcp_pyproject_path, "rb") as f:
37-
mcp_pyproject = tomllib.load(f)
38-
mcp_version = mcp_pyproject["project"]["version"]
32+
with open(root / "gemini-extension.json") as f:
33+
sources["gemini-extension.json version"] = json.load(f)["version"]
3934

40-
server_json_path = root / "futuresearch-mcp" / "server.json"
41-
with open(server_json_path) as f:
35+
with open(root / "futuresearch-mcp" / "pyproject.toml", "rb") as f:
36+
sources["futuresearch-mcp/pyproject.toml version"] = tomllib.load(f)["project"][
37+
"version"
38+
]
39+
40+
with open(root / "futuresearch-mcp" / "server.json") as f:
4241
server_json = json.load(f)
43-
server_json_version = server_json["version"]
44-
server_json_package_version = server_json["packages"][0]["version"]
42+
sources["futuresearch-mcp/server.json version"] = server_json["version"]
43+
sources["futuresearch-mcp/server.json packages[0].version"] = server_json[
44+
"packages"
45+
][0]["version"]
46+
47+
with open(root / "futuresearch-mcp" / "manifest.json") as f:
48+
sources["futuresearch-mcp/manifest.json version"] = json.load(f)["version"]
49+
50+
citation_text = (root / "CITATION.cff").read_text()
51+
citation_match = re.search(r"^version:\s*(.+)$", citation_text, re.MULTILINE)
52+
assert citation_match, "Could not find version in CITATION.cff"
53+
sources["CITATION.cff version"] = citation_match.group(1).strip()
54+
55+
readme_text = (root / "README.md").read_text()
56+
bibtex_match = re.search(
57+
r"@software\{futuresearch,.*?version\s*=\s*\{(.+?)\}", readme_text, re.DOTALL
58+
)
59+
assert bibtex_match, "Could not find BibTeX version in README.md"
60+
sources["README.md BibTeX version"] = bibtex_match.group(1)
4561

46-
manifest_json_path = root / "futuresearch-mcp" / "manifest.json"
47-
with open(manifest_json_path) as f:
48-
manifest_json = json.load(f)
49-
manifest_version = manifest_json["version"]
62+
with open(root / "stubs" / "everyrow" / "pyproject.toml", "rb") as f:
63+
sources["stubs/everyrow/pyproject.toml version"] = tomllib.load(f)["project"][
64+
"version"
65+
]
5066

51-
assert pyproject_version == plugin_version, (
52-
f"pyproject.toml version ({pyproject_version}) != plugin.json version ({plugin_version})"
53-
)
54-
assert pyproject_version == gemini_version, (
55-
f"pyproject.toml version ({pyproject_version}) != gemini-extension.json version ({gemini_version})"
56-
)
57-
assert pyproject_version == marketplace_version, (
58-
f"pyproject.toml version ({pyproject_version}) != marketplace.json version ({marketplace_version})"
59-
)
60-
assert pyproject_version == mcp_version, (
61-
f"pyproject.toml version ({pyproject_version}) != futuresearch-mcp/pyproject.toml version ({mcp_version})"
62-
)
63-
assert pyproject_version == server_json_version, (
64-
f"pyproject.toml version ({pyproject_version}) != futuresearch-mcp/server.json version ({server_json_version})"
65-
)
66-
assert pyproject_version == server_json_package_version, (
67-
f"pyproject.toml version ({pyproject_version}) != futuresearch-mcp/server.json packages[0].version ({server_json_package_version})"
68-
)
69-
assert pyproject_version == manifest_version, (
70-
f"pyproject.toml version ({pyproject_version}) != futuresearch-mcp/manifest.json version ({manifest_version})"
71-
)
72-
assert pyproject_version == futuresearch.__version__, (
73-
f"pyproject.toml version ({pyproject_version}) != futuresearch.__version__ ({futuresearch.__version__})"
67+
with open(root / "stubs" / "everyrow-mcp" / "pyproject.toml", "rb") as f:
68+
sources["stubs/everyrow-mcp/pyproject.toml version"] = tomllib.load(f)[
69+
"project"
70+
]["version"]
71+
72+
sources["futuresearch.__version__"] = futuresearch.__version__
73+
74+
mismatches = {label: v for label, v in sources.items() if v != pyproject_version}
75+
assert not mismatches, (
76+
f"pyproject.toml version is {pyproject_version}, but these differ: {mismatches}"
7477
)
7578

7679

0 commit comments

Comments
 (0)