Skip to content

Commit 974a3b5

Browse files
authored
feat: auto-detect semver bump in /release skill (#496)
* docs: promote #83 (brief command) and #71 (type inference) to Tier 0 in backlog These two items deliver the highest immediate impact on agent experience and graph accuracy without requiring Rust porting or TypeScript migration. They should be implemented before any Phase 4+ roadmap work. - #83: hook-optimized `codegraph brief` enriches passively-injected context - #71: basic type inference closes the biggest resolution gap for TS/Java * docs: add Phase 4 (Native Analysis Acceleration) to roadmap Add new Phase 4 covering the port of JS-only build phases to Rust: - 4.1-4.3: AST nodes, CFG, dataflow visitor ports (~587ms savings) - 4.4: Batch SQLite inserts (~143ms) - 4.5: Role classification & structure (~42ms) - 4.6: Complete complexity pre-computation - 4.7: Fix incremental rebuild data loss on native engine - 4.8: Incremental rebuild performance (target sub-100ms) Bump old Phases 4-10 to 5-11 with all cross-references updated. Benchmark evidence shows ~50% of native build time is spent in JS visitors that run identically on both engines. * docs: fix sub-section numbering to match parent phase headings * fix: align version computation between publish.yml and bench-version.js - Add COMMITS=0 guard in publish.yml to return clean version when HEAD is exactly at a tag (mirrors bench-version.js early return) - Change bench-version.js to use PATCH+1-dev.COMMITS format instead of PATCH+COMMITS-dev.SHA (mirrors publish.yml's new scheme) - Fix fallback in bench-version.js to use dev.1 matching publish.yml's no-tags COMMITS=1 default Impact: 1 functions changed, 0 affected * feat: auto-detect semver bump in /release skill when no version provided The release skill now scans commit history using conventional commit rules to determine major/minor/patch automatically. Explicit version argument still works as before. * fix: restore SHA-based uniqueness in bench-version fallback and add -dev.0 suffix The no-tags fallback hardcoded dev.1, causing repeated benchmark runs to silently overwrite each other. Restore short SHA for uniqueness. Also add -dev.0 suffix when COMMITS=0 so dev builds at an exact tag are never confused with stable releases. Impact: 1 functions changed, 0 affected * fix: add -dev.0 suffix for COMMITS=0 dev builds in publish.yml When HEAD is exactly at a release tag, the dev build emitted a clean semver string identical to the stable release. Add -dev.0 suffix to prevent confusion between dev pre-releases and stable releases.
1 parent 7b6a0ce commit 974a3b5

3 files changed

Lines changed: 51 additions & 17 deletions

File tree

.claude/skills/release/SKILL.md

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,55 @@
11
---
22
name: release
33
description: Prepare a codegraph release — bump versions, update CHANGELOG, ROADMAP, BACKLOG, README, create PR
4-
argument-hint: <version e.g. 3.1.1>
4+
argument-hint: "[version e.g. 3.1.1] (optional — auto-detects from commits)"
55
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent
66
---
77

8-
# Release v$ARGUMENTS
8+
# Release
99

10-
You are preparing a release for `@optave/codegraph` version **$ARGUMENTS**.
10+
You are preparing a release for `@optave/codegraph`.
11+
12+
**Version argument:** `$ARGUMENTS`
13+
- If a version was provided (e.g. `3.1.1`), use it as the target version.
14+
- If no version was provided (empty or blank `$ARGUMENTS`), you will auto-detect it in Step 1b.
1115

1216
---
1317

14-
## Step 1: Gather context
18+
## Step 1a: Gather context
1519

1620
Run these in parallel:
17-
1. `git log --oneline v<previous-tag>..HEAD` — all commits since the last release tag
21+
1. `git log --oneline v<previous-tag>..HEAD` — all commits since the last release tag (use `git describe --tags --match "v*" --abbrev=0` to find the previous tag)
1822
2. Read `CHANGELOG.md` (first 80 lines) — understand the format
1923
3. Read `package.json` — current version
2024
4. `git describe --tags --match "v*" --abbrev=0` — find the previous stable release tag
2125

26+
## Step 1b: Determine version (if not provided)
27+
28+
If `$ARGUMENTS` is empty or blank, determine the semver bump from the commits gathered in Step 1a.
29+
30+
Scan **every commit message** between the last tag and HEAD. Apply these rules in priority order:
31+
32+
| Condition | Bump |
33+
|-----------|------|
34+
| Any commit has a `BREAKING CHANGE:` or `BREAKING-CHANGE:` footer, **or** uses the `!` suffix (e.g. `feat!:`, `fix!:`, `refactor!:`) | **major** |
35+
| Any commit uses `feat:` or `feat(scope):` | **minor** |
36+
| Everything else (`fix:`, `refactor:`, `perf:`, `chore:`, `docs:`, `test:`, `ci:`, etc.) | **patch** |
37+
38+
Given the current version `MAJOR.MINOR.PATCH` from `package.json`, compute the new version:
39+
- **major**`(MAJOR+1).0.0`
40+
- **minor**`MAJOR.(MINOR+1).0`
41+
- **patch**`MAJOR.MINOR.(PATCH+1)`
42+
43+
Print the detected bump reason and the resolved version, e.g.:
44+
> Detected **minor** bump (found `feat:` commits). Version: 3.1.0 → **3.2.0**
45+
46+
Use the resolved version as `VERSION` for all subsequent steps.
47+
48+
If `$ARGUMENTS` was provided, use it directly as `VERSION`.
49+
2250
## Step 2: Bump version in package.json
2351

24-
Edit `package.json` to set `"version": "$ARGUMENTS"`.
52+
Edit `package.json` to set `"version": "VERSION"`.
2553

2654
**Do NOT bump:**
2755
- `crates/codegraph-core/Cargo.toml` — synced automatically by `scripts/sync-native-versions.js` during the publish workflow
@@ -104,16 +132,16 @@ Run `grep` to confirm the new version appears in `package-lock.json` and that al
104132

105133
## Step 8: Create branch, commit, push, PR
106134

107-
1. Create branch: `git checkout -b release/$ARGUMENTS`
135+
1. Create branch: `git checkout -b release/VERSION`
108136
2. Stage only the files you changed: `CHANGELOG.md`, `package.json`, `package-lock.json`, `docs/roadmap/ROADMAP.md`, `docs/roadmap/BACKLOG.md` if changed, `README.md` if changed
109-
3. Commit: `chore: release v$ARGUMENTS`
110-
4. Push: `git push -u origin release/$ARGUMENTS`
137+
3. Commit: `chore: release vVERSION`
138+
4. Push: `git push -u origin release/VERSION`
111139
5. Create PR:
112140

113141
```
114-
gh pr create --title "chore: release v$ARGUMENTS" --body "$(cat <<'EOF'
142+
gh pr create --title "chore: release vVERSION" --body "$(cat <<'EOF'
115143
## Summary
116-
- Bump version to $ARGUMENTS
144+
- Bump version to VERSION
117145
- Add CHANGELOG entry for all commits since previous release
118146
- Update ROADMAP progress
119147

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
8282
fi
8383
if [ "$COMMITS" -eq 0 ]; then
84-
VERSION="${MAJOR}.${MINOR}.${PATCH}"
84+
VERSION="${MAJOR}.${MINOR}.${PATCH}-dev.0"
8585
else
8686
VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))-dev.${COMMITS}"
8787
fi

scripts/bench-version.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 1. `git describe --tags --match "v*" --abbrev=0` → find nearest release tag
66
* 2. `git rev-list <tag>..HEAD --count` → count commits since that tag
77
*
8-
* - If HEAD is exactly tagged (0 commits): returns "3.1.5"
8+
* - If HEAD is exactly tagged (0 commits): returns "3.1.5-dev.0"
99
* - Otherwise: returns "3.1.6-dev.12" (NEXT_PATCH-dev.COMMIT_COUNT)
1010
* This keeps dev versions in the correct semver range between the
1111
* current release and the next, avoiding inflated patch numbers.
@@ -36,8 +36,8 @@ export function getBenchmarkVersion(pkgVersion, cwd) {
3636

3737
const [, major, minor, patch] = m;
3838

39-
// Exact tag (0 commits since tag): return clean release version
40-
if (commits === 0) return `${major}.${minor}.${patch}`;
39+
// Exact tag (0 commits since tag): still mark as dev to avoid confusion with stable
40+
if (commits === 0) return `${major}.${minor}.${patch}-dev.0`;
4141

4242
// Dev build: MAJOR.MINOR.(PATCH+1)-dev.COMMITS
4343
const nextPatch = Number(patch) + 1;
@@ -46,11 +46,17 @@ export function getBenchmarkVersion(pkgVersion, cwd) {
4646
/* git not available or no tags */
4747
}
4848

49-
// Fallback: no git or no tags — match publish.yml's no-tags behavior (COMMITS=1)
49+
// Fallback: no git or no tags — try to get a unique SHA so repeated runs
50+
// don't collide in benchmark reports (which deduplicate by version)
5051
const parts = pkgVersion.split('.');
5152
if (parts.length === 3) {
5253
const [major, minor, patch] = parts;
53-
return `${major}.${minor}.${Number(patch) + 1}-dev.1`;
54+
try {
55+
const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim();
56+
return `${major}.${minor}.${Number(patch) + 1}-dev.${hash}`;
57+
} catch {
58+
return `${major}.${minor}.${Number(patch) + 1}-dev`;
59+
}
5460
}
5561
return `${pkgVersion}-dev`;
5662
}

0 commit comments

Comments
 (0)