Files
thedotmack__claude-mem/scripts/sync-plugin-manifests.js
Alex Newman 0b723c3069 fix: stop sync-plugin-manifests clobbering Cursor/Grok plugin copy
syncCursorPlugin() copied `description` and `homepage` straight from the
root package.json into claude-mem-cursor/.cursor-plugin/plugin.json and
claude-mem-grok-bot/.cursor-plugin/plugin.json. Those are independent
marketplace plugins for products that are not Claude Code, and they carry
their own curated copy from 1c7aaff3 ("Persistent memory for Cursor…",
"Persistent memory for Grok Bot…"). Syncing overwrote both with the
generic "Memory compression system for Claude Code" boilerplate.

The bug stayed invisible through 13.24.0 because that bump was a hand edit
that never ran the build -- the same omission that produced the stale
bundles in #3857. Running `npm run build-and-sync` honestly is what fires
it, so the fix has to land before the next real build or every release
silently reverts the Cursor and Grok Bot listings.

Both fields now fall back to package.json only when the manifest does not
define its own. Version, repository, license and author.name still sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fu9bpdG4AWZNq3m8zkUkYg
2026-09-05 05:22:19 +00:00

123 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, '..');
const packageJsonPath = path.join(rootDir, 'package.json');
const codexPluginPath = path.join(rootDir, '.codex-plugin', 'plugin.json');
const bundledCodexPluginPath = path.join(rootDir, 'plugin', '.codex-plugin', 'plugin.json');
const claudePluginPath = path.join(rootDir, '.claude-plugin', 'plugin.json');
const bundledClaudePluginPath = path.join(rootDir, 'plugin', '.claude-plugin', 'plugin.json');
const cursorPluginPaths = [
path.join(rootDir, 'claude-mem-cursor', '.cursor-plugin', 'plugin.json'),
path.join(rootDir, 'claude-mem-grok-bot', '.cursor-plugin', 'plugin.json'),
];
function readJson(filePath) {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
function writeJson(filePath, value) {
fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n');
}
function syncCodexPlugin(plugin, pkg) {
const author =
typeof plugin.author === 'object' && plugin.author ? plugin.author : {};
return {
...plugin,
name: pkg.name,
version: pkg.version,
description: pkg.description,
homepage: pkg.homepage,
repository: normalizeRepositoryUrl(pkg.repository),
license: pkg.license,
keywords: pkg.keywords,
author: {
...author,
name: normalizeAuthorName(pkg.author),
},
interface: {
...plugin.interface,
developerName: normalizeAuthorName(pkg.author),
websiteURL: normalizeRepositoryUrl(pkg.repository),
},
};
}
function syncClaudePlugin(plugin, pkg) {
return {
...plugin,
name: pkg.name,
version: pkg.version,
description: pkg.description,
homepage: pkg.homepage,
repository: normalizeRepositoryUrl(pkg.repository),
license: pkg.license,
keywords: pkg.keywords,
author: {
...(typeof plugin.author === 'object' && plugin.author ? plugin.author : {}),
name: normalizeAuthorName(pkg.author),
},
};
}
function normalizeAuthorName(author) {
if (typeof author === 'string') return author;
if (author && typeof author === 'object' && typeof author.name === 'string') return author.name;
return '';
}
function syncCursorPlugin(plugin, pkg) {
return {
...plugin,
version: pkg.version,
description: plugin.description ?? pkg.description,
homepage: plugin.homepage ?? pkg.homepage,
repository: normalizeRepositoryUrl(pkg.repository),
license: pkg.license,
author: {
...(typeof plugin.author === 'object' && plugin.author ? plugin.author : {}),
name: normalizeAuthorName(pkg.author),
},
};
}
function normalizeRepositoryUrl(repository) {
if (typeof repository === 'string') return repository.replace(/\.git$/, '');
if (repository && typeof repository === 'object' && typeof repository.url === 'string')
return repository.url.replace(/\.git$/, '');
return '';
}
function main() {
for (const filePath of [packageJsonPath, codexPluginPath, bundledCodexPluginPath, claudePluginPath, bundledClaudePluginPath, ...cursorPluginPaths]) {
if (!fs.existsSync(filePath)) {
console.error(`Missing required file: ${filePath}`);
process.exit(1);
}
}
const pkg = readJson(packageJsonPath);
const codexPlugin = readJson(codexPluginPath);
const bundledCodexPlugin = readJson(bundledCodexPluginPath);
const claudePlugin = readJson(claudePluginPath);
const bundledClaudePlugin = readJson(bundledClaudePluginPath);
const cursorPlugins = cursorPluginPaths.map(readJson);
writeJson(codexPluginPath, syncCodexPlugin(codexPlugin, pkg));
writeJson(bundledCodexPluginPath, syncCodexPlugin(bundledCodexPlugin, pkg));
writeJson(claudePluginPath, syncClaudePlugin(claudePlugin, pkg));
writeJson(bundledClaudePluginPath, syncClaudePlugin(bundledClaudePlugin, pkg));
cursorPluginPaths.forEach((filePath, index) => writeJson(filePath, syncCursorPlugin(cursorPlugins[index], pkg)));
console.log('✓ Synced plugin manifests from package.json');
}
main();