From 221a61fa9107e4438b606251434d6a298225d2b5 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 19:55:38 +0100 Subject: [PATCH 1/4] Fix Propulsion plugin loading --- .agents/plugins/marketplace.json | 2 +- .codex-plugin/plugin.json | 2 +- index.mjs | 14 +++++++++ package.json | 2 +- plugins/propulsion/.codex-plugin | 1 + plugins/propulsion/assets | 1 + plugins/propulsion/hooks | 1 + plugins/propulsion/skills | 1 + tests/opencode-plugin.test.js | 51 ++++++++++++++++++++++++++++++++ 9 files changed, 72 insertions(+), 3 deletions(-) create mode 120000 plugins/propulsion/.codex-plugin create mode 120000 plugins/propulsion/assets create mode 120000 plugins/propulsion/hooks create mode 120000 plugins/propulsion/skills diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index b2412d9..855b046 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -9,7 +9,7 @@ "name": "propulsion", "source": { "source": "local", - "path": "./" + "path": "./plugins/propulsion" }, "policy": { "installation": "AVAILABLE", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 461331f..612d3ad 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.0", + "version": "1.0.1", "description": "Propulsion workflow routing and skills for agentic coding.", "author": { "name": "Moon Pixels" diff --git a/index.mjs b/index.mjs index 5f25e26..2006862 100644 --- a/index.mjs +++ b/index.mjs @@ -1,12 +1,26 @@ import { createRequire } from 'node:module'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; const require = createRequire(import.meta.url); const { getPropulsionBootstrapGuidance, } = require('./lib/bootstrap-guidance.js'); +const PROPULSION_SKILLS_DIR = join( + dirname(fileURLToPath(import.meta.url)), + 'skills', +); async function PropulsionPlugin() { return { + config: async (config) => { + config.skills = config.skills || {}; + config.skills.paths = config.skills.paths || []; + + if (!config.skills.paths.includes(PROPULSION_SKILLS_DIR)) { + config.skills.paths.push(PROPULSION_SKILLS_DIR); + } + }, 'experimental.chat.messages.transform': async (_input, output) => { output.messages = [ { diff --git a/package.json b/package.json index 96a7c84..f361ecc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.0", + "version": "1.0.1", "main": "./index.mjs", "exports": "./index.mjs", "scripts": { diff --git a/plugins/propulsion/.codex-plugin b/plugins/propulsion/.codex-plugin new file mode 120000 index 0000000..c3acab0 --- /dev/null +++ b/plugins/propulsion/.codex-plugin @@ -0,0 +1 @@ +../../.codex-plugin \ No newline at end of file diff --git a/plugins/propulsion/assets b/plugins/propulsion/assets new file mode 120000 index 0000000..41aef43 --- /dev/null +++ b/plugins/propulsion/assets @@ -0,0 +1 @@ +../../assets \ No newline at end of file diff --git a/plugins/propulsion/hooks b/plugins/propulsion/hooks new file mode 120000 index 0000000..2777e15 --- /dev/null +++ b/plugins/propulsion/hooks @@ -0,0 +1 @@ +../../hooks \ No newline at end of file diff --git a/plugins/propulsion/skills b/plugins/propulsion/skills new file mode 120000 index 0000000..5dcab58 --- /dev/null +++ b/plugins/propulsion/skills @@ -0,0 +1 @@ +../../skills \ No newline at end of file diff --git a/tests/opencode-plugin.test.js b/tests/opencode-plugin.test.js index a76c886..9f413f7 100644 --- a/tests/opencode-plugin.test.js +++ b/tests/opencode-plugin.test.js @@ -1,5 +1,7 @@ import { describe, expect, test } from 'bun:test'; import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { getPropulsionBootstrapGuidance } from '../lib/bootstrap-guidance.js'; @@ -21,6 +23,55 @@ describe('OpenCode Propulsion bootstrap guidance', () => { ).toBe(true); }); + test('publishes Propulsion through a non-empty Codex marketplace plugin path', async () => { + const marketplace = JSON.parse( + await readFile('.agents/plugins/marketplace.json', 'utf8'), + ); + const plugin = marketplace.plugins.find( + ({ name }) => name === 'propulsion', + ); + + expect(plugin.source).toEqual({ + source: 'local', + path: './plugins/propulsion', + }); + + const pluginManifest = JSON.parse( + await readFile( + 'plugins/propulsion/.codex-plugin/plugin.json', + 'utf8', + ), + ); + + expect(pluginManifest.name).toBe('propulsion'); + }); + + test('registers bundled skills with OpenCode config', async () => { + const pluginPackage = (await import('../index.mjs')).default; + const PropulsionPlugin = pluginPackage.server; + const hooks = await PropulsionPlugin({}); + const config = {}; + const skillsDir = join( + dirname(fileURLToPath(import.meta.url)), + '..', + 'skills', + ); + + expect(hooks).toEqual( + expect.objectContaining({ + config: expect.any(Function), + }), + ); + + await hooks.config(config); + await hooks.config(config); + + expect(config.skills.paths).toEqual([skillsDir]); + await expect( + readFile(join(skillsDir, 'debug', 'SKILL.md'), 'utf8'), + ).resolves.toContain('# Debug'); + }); + test('provides high-priority Propulsion routing guidance', () => { const guidance = getPropulsionBootstrapGuidance(); From 98757d5eaa7d1af6977e4daae1502b33133d3aa4 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 20:42:49 +0100 Subject: [PATCH 2/4] Update OpenCode plugin bootstrap and logo metadata --- .codex-plugin/plugin.json | 2 +- index.mjs | 32 ++++++++++++++++++++++++------- tests/opencode-plugin.test.js | 36 +++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 612d3ad..9546f9f 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -41,7 +41,7 @@ ], "brandColor": "#FF4F00", "composerIcon": "./assets/propulsion_icon_square.png", - "logo": "./assets/banner.png", + "logo": "./assets/propulsion_icon_square.png", "screenshots": [] } } diff --git a/index.mjs b/index.mjs index 2006862..d4f3f13 100644 --- a/index.mjs +++ b/index.mjs @@ -22,13 +22,31 @@ async function PropulsionPlugin() { } }, 'experimental.chat.messages.transform': async (_input, output) => { - output.messages = [ - { - role: 'system', - content: getPropulsionBootstrapGuidance(), - }, - ...(output.messages ?? []), - ]; + const bootstrap = getPropulsionBootstrapGuidance(); + const firstUser = output.messages?.find( + (message) => message.info.role === 'user', + ); + + if (!firstUser?.parts?.length) { + return; + } + + if ( + firstUser.parts.some( + (part) => + part.type === 'text' && + part.text.includes(''), + ) + ) { + return; + } + + const ref = firstUser.parts[0]; + firstUser.parts.unshift({ + ...ref, + type: 'text', + text: bootstrap, + }); }, }; } diff --git a/tests/opencode-plugin.test.js b/tests/opencode-plugin.test.js index 9f413f7..7f09904 100644 --- a/tests/opencode-plugin.test.js +++ b/tests/opencode-plugin.test.js @@ -86,13 +86,33 @@ describe('OpenCode Propulsion bootstrap guidance', () => { ); }); - test('registers an OpenCode messages transform that prepends Propulsion guidance', async () => { + test('registers an OpenCode messages transform that injects Propulsion guidance into the first user message', async () => { const pluginPackage = (await import('../index.mjs')).default; const PropulsionPlugin = pluginPackage.server; const hooks = await PropulsionPlugin({}); + const userPart = { + id: 'part-user', + type: 'text', + text: 'Build the thing', + }; const output = { system: ['existing system prompt'], - messages: [{ role: 'user', content: 'Build the thing' }], + messages: [ + { + info: { role: 'assistant' }, + parts: [ + { + id: 'part-assistant', + type: 'text', + text: 'Ready', + }, + ], + }, + { + info: { role: 'user' }, + parts: [userPart], + }, + ], }; expect(hooks).toEqual( @@ -102,15 +122,19 @@ describe('OpenCode Propulsion bootstrap guidance', () => { ); expect(hooks).not.toHaveProperty('experimental.chat.system.transform'); + await hooks['experimental.chat.messages.transform']({}, output); await hooks['experimental.chat.messages.transform']({}, output); expect(output.system).toEqual(['existing system prompt']); - expect(output.messages).toEqual([ + expect(output.messages).toHaveLength(2); + expect(output.messages[0].info.role).toBe('assistant'); + expect(output.messages[1].parts).toEqual([ { - role: 'system', - content: getPropulsionBootstrapGuidance(), + ...userPart, + type: 'text', + text: getPropulsionBootstrapGuidance(), }, - { role: 'user', content: 'Build the thing' }, + userPart, ]); }); }); From 32b6099e45b50dfe3442704cfae824305225d84c Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 20:52:21 +0100 Subject: [PATCH 3/4] Update Propulsion skill description --- skills/propulsion/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/propulsion/SKILL.md b/skills/propulsion/SKILL.md index 83b91ca..d0afa65 100644 --- a/skills/propulsion/SKILL.md +++ b/skills/propulsion/SKILL.md @@ -1,7 +1,7 @@ --- name: propulsion # prettier-ignore -description: Manage software-work request routing into Propulsion before any downstream stage is entered. Use when starting a session with software work. +description: Execute structured AI software development from planning through execution. Use when implementing or updating code, debugging issues, or starting repo software work. --- # Propulsion From 87680e9841b2b4e2a2ffc72d286fcc2252fc5e10 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 20:54:23 +0100 Subject: [PATCH 4/4] Bump version --- .codex-plugin/plugin.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 9546f9f..4835c37 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.1", + "version": "1.0.2", "description": "Propulsion workflow routing and skills for agentic coding.", "author": { "name": "Moon Pixels" diff --git a/package.json b/package.json index f361ecc..e306ede 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.1", + "version": "1.0.2", "main": "./index.mjs", "exports": "./index.mjs", "scripts": {