aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
147 lines (129 loc) • 4.35 kB
JavaScript
/**
* Verify release version metadata matches package.json version.
*
* PUW-038 (#1139): package-lock.json and the marketplace manifest's top-level
* metadata.version must move in lockstep with package.json on every release;
* otherwise installs and plugin surfaces can report stale versions while npm
* ships the new one.
*
* Exits 0 when versions match. For marketplace metadata only, pre-release
* suffixes may differ when `--allow-prerelease-mismatch` is passed; package
* lockfiles must always match package.json exactly.
*
* Usage:
* node tools/workspace/check-marketplace-version.mjs
* node tools/workspace/check-marketplace-version.mjs --allow-prerelease-mismatch
*/
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = resolve(__dirname, '..', '..');
function strip(version) {
// Drop pre-release/build suffix (-rc.1, -alpha.2, +build.42, etc.)
return version.replace(/[-+].*$/, '');
}
function readJson(root, relativePath) {
return JSON.parse(readFileSync(resolve(root, relativePath), 'utf8'));
}
export function checkVersionLockstep(
root = REPO_ROOT,
{ allowPrereleaseMismatch = false } = {},
) {
const pkg = readJson(root, 'package.json');
const lock = readJson(root, 'package-lock.json');
const marketplace = JSON.parse(
readFileSync(resolve(root, '.claude-plugin/marketplace.json'), 'utf8'),
);
const pkgVersion = pkg.version;
const lockVersion = lock.version;
const lockRootVersion = lock?.packages?.['']?.version;
const marketplaceVersion = marketplace?.metadata?.version;
if (!pkgVersion) {
return {
ok: false,
message: 'FAIL: package.json has no version field',
};
}
if (!lockVersion) {
return {
ok: false,
message: 'FAIL: package-lock.json has no top-level version field',
};
}
if (!lockRootVersion) {
return {
ok: false,
message: 'FAIL: package-lock.json packages[""].version missing',
};
}
if (!marketplaceVersion) {
return {
ok: false,
message: 'FAIL: .claude-plugin/marketplace.json metadata.version missing',
};
}
if (lockVersion !== pkgVersion) {
return {
ok: false,
message:
`FAIL: package-lock.json version (${lockVersion}) does not match ` +
`package.json (${pkgVersion}).`,
fix: `Fix: update package-lock.json version to ${pkgVersion}.`,
};
}
if (lockRootVersion !== pkgVersion) {
return {
ok: false,
message:
`FAIL: package-lock.json packages[""].version (${lockRootVersion}) does not match ` +
`package.json (${pkgVersion}).`,
fix: `Fix: update package-lock.json packages[""].version to ${pkgVersion}.`,
};
}
const exactMatch = pkgVersion === marketplaceVersion;
const stableMatch = strip(pkgVersion) === strip(marketplaceVersion);
if (exactMatch) {
return {
ok: true,
message:
`OK package-lock.json and marketplace metadata.version (${marketplaceVersion}) ` +
`match package.json (${pkgVersion})`,
};
}
if (allowPrereleaseMismatch && stableMatch) {
return {
ok: true,
message:
`OK package-lock.json matches package.json (${pkgVersion}); ` +
`marketplace metadata.version (${marketplaceVersion}) matches stable line ` +
'with pre-release suffix difference allowed by --allow-prerelease-mismatch',
};
}
return {
ok: false,
message:
`FAIL: marketplace metadata.version (${marketplaceVersion}) does not match ` +
`package.json (${pkgVersion}). PUW-038 (#1139) requires lockstep bumps.`,
fix: `Fix: update .claude-plugin/marketplace.json metadata.version to ${pkgVersion}.`,
};
}
function main() {
const args = process.argv.slice(2);
const allowPrereleaseMismatch = args.includes('--allow-prerelease-mismatch');
const result = checkVersionLockstep(REPO_ROOT, { allowPrereleaseMismatch });
if (result.ok) {
console.log(result.message);
process.exit(0);
}
console.error(result.message);
if (result.fix) {
console.error('');
console.error(result.fix);
}
process.exit(1);
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}