From 0759cb473d73c546ffe43afd5c0275945b59ac8a Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Tue, 5 May 2026 08:21:04 +0100 Subject: [PATCH 1/4] Fix Propulsion hook plugin root path --- hooks/hooks.json | 4 +++- tests/codex-hook.test.js | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/hooks/hooks.json b/hooks/hooks.json index 1668310..ef959fa 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -6,7 +6,9 @@ "hooks": [ { "type": "command", - "command": "./hooks/run-hook.cmd session-start" + "command": "\"${CODEX_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start", + "timeout": 10, + "statusMessage": "Loading Propulsion workflow" } ] } diff --git a/tests/codex-hook.test.js b/tests/codex-hook.test.js index 09d74b8..ca70fab 100644 --- a/tests/codex-hook.test.js +++ b/tests/codex-hook.test.js @@ -17,7 +17,7 @@ describe('Codex Propulsion bootstrap guidance', () => { ); }); - test('registers a compact plugin-local session-start hook matcher', async () => { + test('registers a plugin-root session-start hook matcher', async () => { const config = await readJson('hooks/hooks.json'); expect(config.hooks.SessionStart).toEqual([ @@ -26,13 +26,42 @@ describe('Codex Propulsion bootstrap guidance', () => { hooks: [ { type: 'command', - command: './hooks/run-hook.cmd session-start', + command: + '"${CODEX_PLUGIN_ROOT}/hooks/run-hook.cmd" session-start', + timeout: 10, + statusMessage: 'Loading Propulsion workflow', }, ], }, ]); }); + test('runs configured hook command from outside the plugin cwd', async () => { + const config = await readJson('hooks/hooks.json'); + const command = config.hooks.SessionStart[0].hooks[0].command; + + const result = Bun.spawnSync({ + cmd: ['sh', '-c', command], + cwd: '/private/tmp', + env: { + ...process.env, + CODEX_PLUGIN_ROOT: process.cwd(), + }, + stdout: 'pipe', + stderr: 'pipe', + }); + + expect(result.exitCode).toBe(0); + + const output = new TextDecoder().decode(result.stdout).trim(); + const payload = JSON.parse(output); + + expect(payload.hookSpecificOutput).toEqual({ + hookEventName: 'SessionStart', + additionalContext: PROPULSION_BOOTSTRAP_GUIDANCE, + }); + }); + test('prints Codex SessionStart additional context as parseable JSON', async () => { const result = Bun.spawnSync({ cmd: ['./hooks/run-hook.cmd', 'session-start'], From 1852fbbb28c1c50dd87a9f83023c23671059ed39 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Tue, 5 May 2026 09:22:31 +0100 Subject: [PATCH 2/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 e9fbdc2..166b797 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.3", + "version": "1.0.4", "description": "Opinionated agentic coding workflow that guides software work from exploration and planning through execution and review.", "author": { "name": "Moon Pixels" diff --git a/package.json b/package.json index e20b758..06e1d4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.3", + "version": "1.0.4", "main": "./index.mjs", "exports": "./index.mjs", "scripts": { From e1b19be8971b3ef7940706b0423aa43afb408e92 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Tue, 5 May 2026 09:32:54 +0100 Subject: [PATCH 3/4] Support Claude plugin root hook fallback --- .codex-plugin/plugin.json | 2 +- hooks/hooks.json | 2 +- package.json | 2 +- tests/codex-hook.test.js | 52 ++++++++++++++++++++++++++------------- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 166b797..d78cda5 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.4", + "version": "1.0.5", "description": "Opinionated agentic coding workflow that guides software work from exploration and planning through execution and review.", "author": { "name": "Moon Pixels" diff --git a/hooks/hooks.json b/hooks/hooks.json index ef959fa..499e20f 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "\"${CODEX_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start", + "command": "\"${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/hooks/run-hook.cmd\" session-start", "timeout": 10, "statusMessage": "Loading Propulsion workflow" } diff --git a/package.json b/package.json index 06e1d4c..50a5a29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "propulsion", - "version": "1.0.4", + "version": "1.0.5", "main": "./index.mjs", "exports": "./index.mjs", "scripts": { diff --git a/tests/codex-hook.test.js b/tests/codex-hook.test.js index ca70fab..a2cbad0 100644 --- a/tests/codex-hook.test.js +++ b/tests/codex-hook.test.js @@ -10,6 +10,27 @@ async function readJson(path) { return JSON.parse(await readFile(path, 'utf8')); } +async function runConfiguredHook(env) { + const config = await readJson('hooks/hooks.json'); + const command = config.hooks.SessionStart[0].hooks[0].command; + + const result = Bun.spawnSync({ + cmd: ['sh', '-c', command], + cwd: '/private/tmp', + env: { + ...process.env, + ...env, + }, + stdout: 'pipe', + stderr: 'pipe', + }); + + expect(result.exitCode).toBe(0); + + const output = new TextDecoder().decode(result.stdout).trim(); + return JSON.parse(output); +} + describe('Codex Propulsion bootstrap guidance', () => { test('uses the shared Propulsion bootstrap contract', () => { expect(getPropulsionBootstrapGuidance()).toBe( @@ -27,7 +48,7 @@ describe('Codex Propulsion bootstrap guidance', () => { { type: 'command', command: - '"${CODEX_PLUGIN_ROOT}/hooks/run-hook.cmd" session-start', + '"${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/hooks/run-hook.cmd" session-start', timeout: 10, statusMessage: 'Loading Propulsion workflow', }, @@ -36,25 +57,22 @@ describe('Codex Propulsion bootstrap guidance', () => { ]); }); - test('runs configured hook command from outside the plugin cwd', async () => { - const config = await readJson('hooks/hooks.json'); - const command = config.hooks.SessionStart[0].hooks[0].command; - - const result = Bun.spawnSync({ - cmd: ['sh', '-c', command], - cwd: '/private/tmp', - env: { - ...process.env, - CODEX_PLUGIN_ROOT: process.cwd(), - }, - stdout: 'pipe', - stderr: 'pipe', + test('runs configured hook command with CODEX_PLUGIN_ROOT', async () => { + const payload = await runConfiguredHook({ + CODEX_PLUGIN_ROOT: process.cwd(), }); - expect(result.exitCode).toBe(0); + expect(payload.hookSpecificOutput).toEqual({ + hookEventName: 'SessionStart', + additionalContext: PROPULSION_BOOTSTRAP_GUIDANCE, + }); + }); - const output = new TextDecoder().decode(result.stdout).trim(); - const payload = JSON.parse(output); + test('runs configured hook command with CLAUDE_PLUGIN_ROOT fallback', async () => { + const payload = await runConfiguredHook({ + CODEX_PLUGIN_ROOT: '', + CLAUDE_PLUGIN_ROOT: process.cwd(), + }); expect(payload.hookSpecificOutput).toEqual({ hookEventName: 'SessionStart', From 7d1e25615c5719bc428106703acff9b5362c5cdf Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter Date: Tue, 5 May 2026 09:33:36 +0100 Subject: [PATCH 4/4] Document sandbox fallback for gh auth --- skills/pr/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/pr/SKILL.md b/skills/pr/SKILL.md index 4d6141a..fa173ab 100644 --- a/skills/pr/SKILL.md +++ b/skills/pr/SKILL.md @@ -11,7 +11,7 @@ Create or reuse one GitHub pull request from the current branch and report the v ALL prerequisites MUST be true before following this skill. -- GitHub CLI `gh` is installed and authenticated for the target repository. +- GitHub CLI `gh` is installed and authenticated for the target repository (may need to run outside sandbox). - The current directory is a git repository with an `origin` remote. ## Instructions