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/7] 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/7] 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/7] 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/7] 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": { From ec4fc6ca16e901570b35fa154e1c528f7960d7e8 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 22:17:20 +0100 Subject: [PATCH 5/7] Update plugin metadata and remote loading --- .agents/plugins/marketplace.json | 5 +++-- .codex-plugin/plugin.json | 30 +++++++++++++++++------------- 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 | 23 ----------------------- 8 files changed, 21 insertions(+), 43 deletions(-) delete mode 120000 plugins/propulsion/.codex-plugin delete mode 120000 plugins/propulsion/assets delete mode 120000 plugins/propulsion/hooks delete mode 120000 plugins/propulsion/skills diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 855b046..224097e 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -8,8 +8,9 @@ { "name": "propulsion", "source": { - "source": "local", - "path": "./plugins/propulsion" + "source": "url", + "url": "https://github.com/moonpixels/propulsion.git", + "ref": "main" }, "policy": { "installation": "AVAILABLE", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 4835c37..e9fbdc2 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "propulsion", - "version": "1.0.2", - "description": "Propulsion workflow routing and skills for agentic coding.", + "version": "1.0.3", + "description": "Opinionated agentic coding workflow that guides software work from exploration and planning through execution and review.", "author": { "name": "Moon Pixels" }, @@ -10,34 +10,38 @@ "license": "MIT", "keywords": [ "propulsion", - "codex", - "codex-plugin", - "opencode", - "opencode-plugin", "agentic-coding", + "software-development", "skills", "workflow", + "exploration", "planning", + "execution", + "review", + "brainstorm", + "interrogate", + "plan", + "execute", + "debug", "tdd", "debugging", - "review", - "guardrails", + "prd", "developer-tools" ], "skills": "./skills/", "hooks": "./hooks/hooks.json", "interface": { "displayName": "Propulsion", - "shortDescription": "Workflow routing and skills for agentic coding.", - "longDescription": "Propulsion adds a compact workflow skill set and high-priority startup routing guidance while preserving Codex tools and permissions.", + "shortDescription": "Opinionated workflow for agentic software development.", + "longDescription": "Propulsion automatically guides ordinary software requests through an opinionated agentic coding workflow: explore the codebase, resolve scope, create a plan, execute in focused slices, and review the result before handoff. It keeps agents grounded in repo context, explicit decisions, tests, and review loops.", "developerName": "Moon Pixels", "category": "Coding", "capabilities": ["Interactive", "Read", "Write"], "websiteURL": "https://github.com/moonpixels/propulsion", "defaultPrompt": [ - "Turn my idea into an implementation-ready plan with Propulsion", - "Debug and fix this bug with Propulsion guardrails", - "Review these changes with the Propulsion review workflow" + "Implement social auth with Google and GitHub", + "Update the app to support team workspaces and role-based access", + "Fix the bug causing saved settings to reset after refresh" ], "brandColor": "#FF4F00", "composerIcon": "./assets/propulsion_icon_square.png", diff --git a/package.json b/package.json index e306ede..e20b758 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.2", + "version": "1.0.3", "main": "./index.mjs", "exports": "./index.mjs", "scripts": { diff --git a/plugins/propulsion/.codex-plugin b/plugins/propulsion/.codex-plugin deleted file mode 120000 index c3acab0..0000000 --- a/plugins/propulsion/.codex-plugin +++ /dev/null @@ -1 +0,0 @@ -../../.codex-plugin \ No newline at end of file diff --git a/plugins/propulsion/assets b/plugins/propulsion/assets deleted file mode 120000 index 41aef43..0000000 --- a/plugins/propulsion/assets +++ /dev/null @@ -1 +0,0 @@ -../../assets \ No newline at end of file diff --git a/plugins/propulsion/hooks b/plugins/propulsion/hooks deleted file mode 120000 index 2777e15..0000000 --- a/plugins/propulsion/hooks +++ /dev/null @@ -1 +0,0 @@ -../../hooks \ No newline at end of file diff --git a/plugins/propulsion/skills b/plugins/propulsion/skills deleted file mode 120000 index 5dcab58..0000000 --- a/plugins/propulsion/skills +++ /dev/null @@ -1 +0,0 @@ -../../skills \ No newline at end of file diff --git a/tests/opencode-plugin.test.js b/tests/opencode-plugin.test.js index 7f09904..ed195d4 100644 --- a/tests/opencode-plugin.test.js +++ b/tests/opencode-plugin.test.js @@ -23,29 +23,6 @@ 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; From 7688fd48d3140a84ee2d1bd3a5c68150af02055f Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 22:20:10 +0100 Subject: [PATCH 6/7] Update wording --- skills/interrogate/references/interrogate-protocol.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/skills/interrogate/references/interrogate-protocol.md b/skills/interrogate/references/interrogate-protocol.md index 25981c6..31485f9 100644 --- a/skills/interrogate/references/interrogate-protocol.md +++ b/skills/interrogate/references/interrogate-protocol.md @@ -21,11 +21,9 @@ Relentlessly ask the user one question at a time. Keep each question decision-or ```markdown Question: -Recommendation: - Options: -- +- (recommended) - - - From 3f703aaecb69419a3ac3a22a1fe5274c9c5d970c Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Mon, 4 May 2026 22:23:18 +0100 Subject: [PATCH 7/7] Update wording --- skills/propulsion/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/propulsion/SKILL.md b/skills/propulsion/SKILL.md index d0afa65..b4944fb 100644 --- a/skills/propulsion/SKILL.md +++ b/skills/propulsion/SKILL.md @@ -31,8 +31,8 @@ Follow these steps IN ORDER. Do NOT skip steps. These rules are MANDATORY. - User instructions, repository rules, and `AGENTS.md` take priority, then Propulsion skills, then default system behaviour. -- Concrete failures route to `debug`: bug reports, regressions, failing tests, failing builds, runtime errors, crashes, broken behaviour, and diagnosis requests. -- Feature and product-scope work routes to `brainstorm`: new features, unclear scope, UX/product shaping, requirements discovery, PRDs, and broad implementation requests. +- Concrete failures route to `debug`: bug reports, regressions, failing tests, failing builds, runtime errors, crashes etc. +- Feature and product-scope work routes to `brainstorm`: new features, unclear scope, UX/product shaping, requirements discovery, behaviour changes, refactors, optimisations etc. - If `propulsion` applies, route first. Do NOT reload `propulsion` or skip it because the task looks small, obvious, or familiar. - Once following Propulsion workflow, DO NOT leave it until completion. ALWAYS follow the rules of each skill. - ALWAYS route first. DO NOT rationalise skipping with thoughts like: "I need more context first", "I'll inspect the repo first", "This is too small for Propulsion". They are all FALSE.