From 29c0b1b7db4b8ab488c7ef99bef93068576a60fa Mon Sep 17 00:00:00 2001 From: Drew Ritter Date: Tue, 16 Jun 2026 12:13:43 -0700 Subject: [PATCH] fix: read Codex plugin version from manifest (PRI-2240) --- skills/brainstorming/scripts/server.cjs | 22 ++++++---- tests/brainstorm-server/branding.test.js | 53 ++++++++++++++++++++---- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/skills/brainstorming/scripts/server.cjs b/skills/brainstorming/scripts/server.cjs index a4e18284..a828b35a 100644 --- a/skills/brainstorming/scripts/server.cjs +++ b/skills/brainstorming/scripts/server.cjs @@ -206,14 +206,22 @@ const helperInjection = ''; // ========== Helper Functions ========== function readSuperpowersVersion() { - try { - const packageJson = JSON.parse( - fs.readFileSync(path.join(__dirname, '../../..', 'package.json'), 'utf-8') - ); - return String(packageJson.version || 'unknown'); - } catch (e) { - return 'unknown'; + const root = path.join(__dirname, '../../..'); + const manifests = [ + path.join(root, 'package.json'), + path.join(root, '.codex-plugin/plugin.json') + ]; + + for (const manifest of manifests) { + try { + const data = JSON.parse(fs.readFileSync(manifest, 'utf-8')); + if (data.version) return String(data.version); + } catch (e) { + // Packaged Codex plugins omit package.json; try the next manifest. + } } + + return 'unknown'; } function isTruthyEnv(value) { diff --git a/tests/brainstorm-server/branding.test.js b/tests/brainstorm-server/branding.test.js index 6e5b6362..6acda4e3 100644 --- a/tests/brainstorm-server/branding.test.js +++ b/tests/brainstorm-server/branding.test.js @@ -26,9 +26,9 @@ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } -function startServer({ port, dir, env = {} }) { +function startServer({ port, dir, env = {}, serverPath = SERVER_PATH }) { cleanup(dir); - return spawn('node', [SERVER_PATH], { + return spawn('node', [serverPath], { env: { ...process.env, BRAINSTORM_PORT: String(port), @@ -74,6 +74,21 @@ function writeFragment(dir) { fs.writeFileSync(path.join(contentDir, 'screen.html'), '

Pick a layout

'); } +function createPackagedServerFixture(version) { + const root = fs.mkdtempSync(path.join('/tmp', 'superpowers-packaged-server-')); + const scriptDir = path.join(root, 'skills/brainstorming/scripts'); + fs.cpSync(path.join(REPO_ROOT, 'skills/brainstorming/scripts'), scriptDir, { recursive: true }); + fs.mkdirSync(path.join(root, '.codex-plugin'), { recursive: true }); + fs.writeFileSync( + path.join(root, '.codex-plugin/plugin.json'), + JSON.stringify({ name: 'superpowers', version }, null, 2) + ); + return { + root, + serverPath: path.join(scriptDir, 'server.cjs') + }; +} + async function withServer(options, fn) { const server = startServer(options); try { @@ -104,13 +119,13 @@ async function test(name, fn) { } } -function assertBrandedWithLogo(html) { +function assertBrandedWithLogo(html, version = PACKAGE_VERSION) { assert( - html.includes(`Superpowers v${PACKAGE_VERSION}`), + html.includes(`Superpowers v${version}`), 'branding text should include dynamic package version' ); assert( - !html.includes(`Superpowers v${PACKAGE_VERSION} by`), + !html.includes(`Superpowers v${version} by`), 'branding text should not include "by" when the logo is visible' ); assert( @@ -139,15 +154,15 @@ function assertBrandedWithLogo(html) { ); } -function assertBrandedFallbackText(html) { +function assertBrandedFallbackText(html, version = PACKAGE_VERSION) { assert( - html.includes(`Prime Radiant Superpowers v${PACKAGE_VERSION}`), + html.includes(`Prime Radiant Superpowers v${version}`), 'disabled telemetry should keep plain text Prime Radiant/Superpowers branding' ); } -function assertTelemetryImage(html) { - const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(PACKAGE_VERSION)}`; +function assertTelemetryImage(html, version = PACKAGE_VERSION) { + const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(version)}`; assert(html.includes(`src="${expectedUrl}"`), 'remote image should use the dedicated main-domain asset with only v='); assert(!html.includes('event='), 'remote image URL must not include event='); assert(!html.includes('surface='), 'remote image URL must not include surface='); @@ -255,6 +270,26 @@ async function main() { }); }); + await test('packaged Codex plugin reads version from .codex-plugin manifest', async () => { + const port = 3457; + const dir = '/tmp/brainstorm-branding-packaged-codex'; + const packagedVersion = '7.8.9'; + const fixture = createPackagedServerFixture(packagedVersion); + + try { + await withServer({ port, dir, serverPath: fixture.serverPath }, async () => { + writeFragment(dir); + await sleep(300); + const html = await fetchHtml(port); + assertBrandedWithLogo(html, packagedVersion); + assertTelemetryImage(html, packagedVersion); + assert(!html.includes('Superpowers vunknown'), 'packaged plugin should not fall back to unknown version'); + }); + } finally { + cleanup(fixture.root); + } + }); + await test('SUPERPOWERS_DISABLE_TELEMETRY=true omits remote image but keeps local branding', async () => { const port = 3453; const dir = '/tmp/brainstorm-branding-disabled';