Refactor: share version-library grouping and bumpPatch

Extracts the per-key version grouping + same-version dedupe (org-owned shadows
builtin) into lib/versionedLibrary.groupVersions and the semver patch bump into
lib/semver.bumpPatch, both of which were duplicated byte-for-byte across the
Skills and Agent-profiles pages. One source of truth so the two libraries can't
drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-15 15:42:40 +03:30
parent 4758e4b5de
commit 2ac1b6aa18
4 changed files with 58 additions and 64 deletions
+13
View File
@@ -0,0 +1,13 @@
/** Bump the last numeric segment of a semver-ish string (1.0.0 → 1.0.1). Shared by the skill and
* agent-profile "new version" flows so the bump rule stays identical. */
export function bumpPatch(version: string): string {
const parts = version.split('.')
for (let i = parts.length - 1; i >= 0; i--) {
const n = Number(parts[i])
if (Number.isInteger(n)) {
parts[i] = String(n + 1)
return parts.join('.')
}
}
return `${version}.1`
}
+37
View File
@@ -0,0 +1,37 @@
/**
* Shared grouping for the versioned libraries (skills and agent profiles). Both pages show one card
* per key with a version picker, and both must collapse a builtin that an org has forked at the same
* version — the org's own copy shadows the builtin (it's the one that resolves at run time and the
* one you can edit), keeping the picker unambiguous. Kept in one place so the two libraries can't drift.
*/
export interface VersionedItem {
version: string
origin: string
}
/** Group items by key, dedupe per version (org-owned shadows builtin), and sort keys alphabetically. */
export function groupVersions<T extends VersionedItem>(
items: T[],
keyOf: (item: T) => string,
): [string, T[]][] {
const byKey = new Map<string, T[]>()
for (const item of items) {
const key = keyOf(item)
const list = byKey.get(key) ?? []
list.push(item)
byKey.set(key, list)
}
for (const [key, list] of byKey) {
const perVersion = new Map<string, T>()
for (const item of list) {
const existing = perVersion.get(item.version)
if (!existing || (existing.origin === 'Builtin' && item.origin !== 'Builtin')) {
perVersion.set(item.version, item)
}
}
byKey.set(key, [...perVersion.values()])
}
return [...byKey.entries()].sort((a, b) => a[0].localeCompare(b[0]))
}