diff --git a/CHANGELOG.md b/CHANGELOG.md index bed2e90..8bf592a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,10 @@ product overview. ## 0.5.1 — Unreleased +- Manage plugin packages through existing skill sources, review, templates and channel grants. + Compile native Claude hooks as an inline event map so SessionStart hooks execute correctly; + keep executable hooks restricted to authorized live admin turns. + - Fix repeated native file creation and inline editing exhausting Slack’s modal stack. Successful forms return to the existing browser/preview; Cancel and validation retain their prior behavior. diff --git a/FEATURES.md b/FEATURES.md index af77ec6..3712910 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -6,6 +6,7 @@ Git and folder skill sources recognize Claude/Codex plugin manifests and store a as one revision. Existing Review, rollback, templates, organization/channel/personal grants, and source sync govern the package. Catalog and assignment pickers show Plugin badges and safe component summaries. Claude loads approved native skills/commands/agents and admin-gated hooks; +compiled inline hooks use Claude's event map so approved hooks execute instead of being ignored. Codex receives approved skill catalogs. Both receive supported MCP transports through explicit namespaced grants. Unsupported components/authentication produce actionable failures. Compiled files stay inside the channel container's artifact mounts; updates rotate warm snapshots and diff --git a/TEST-PLAN.md b/TEST-PLAN.md index 0d12c2c..5f10f92 100644 --- a/TEST-PLAN.md +++ b/TEST-PLAN.md @@ -42,9 +42,13 @@ MCP tools. Verify personal artifact cleanup after the first author's run complet **PLUG-03 — native components and compatibility (both engines, with different expectations).** Import and approve `test/fixtures/plugins/native/`. Grant it only to disposable test conversations. As an admin in a Full-access Claude conversation, start a fresh thread and ask “Use the native -plugin marker command and ask its plugin-inspector agent for its marker.” Require the command +plugin marker command.” In a separate fresh thread ask “Ask the plugin-inspector agent for its +own marker; do not invoke the marker command.” Separate threads keep command instructions from +contaminating the agent probe. Require the command result `PLUGIN-COMMAND`, observed agent invocation/result `PLUGIN-AGENT`, and exactly one new -`PLUGIN-HOOK` line in that conversation's `plugin-hook-marker.txt` on session start. Repeat as a +`PLUGIN-HOOK` line in that conversation's `plugin-hook-marker.txt` per session start. The compiled +manifest's `hooks` property must contain the event map directly; a nested `hooks.hooks` object +is silently ignored by the CLI and is a failure even if manifest validation passes. Repeat as a non-admin and as an unattended scheduled/background run: require a hook-policy error and no new line. Pin Codex and repeat: require an explicit unsupported native component error before the engine starts. Add an apps or LSP declaration to a disposable source revision and approve it: diff --git a/src/gateway/plugin-runtime.js b/src/gateway/plugin-runtime.js index 2362db8..ade7973 100644 --- a/src/gateway/plugin-runtime.js +++ b/src/gateway/plugin-runtime.js @@ -102,7 +102,7 @@ export function compilePluginPackage(pkg, { capabilities, allowBypass = false, w merged[event] = [...(merged[event] || []), ...entries]; } } - native.hooks = { hooks: merged }; + native.hooks = merged; } const servers = []; const seen = new Set(); diff --git a/test/plugin-runtime.test.js b/test/plugin-runtime.test.js index 97f5433..43073a1 100644 --- a/test/plugin-runtime.test.js +++ b/test/plugin-runtime.test.js @@ -49,7 +49,7 @@ test("hooks require live admin bypass and are declared once without implicit hoo const compiled = compile({}, files, { allowBypass: true }); assert.equal(compiled.files.some((f) => f.path === "hooks/hooks.json"), false); const native = JSON.parse(compiled.files.find((f) => f.path === ".claude-plugin/plugin.json").content); - assert.deepEqual(native.hooks, hooks); + assert.deepEqual(native.hooks, hooks.hooks); assert.throws(() => compile({ hooks: { Stop: "bad" } }, [], { allowBypass: true }), /invalid hook event/); }); diff --git a/test/skills-plugin-import.test.js b/test/skills-plugin-import.test.js index e87a31b..74549bd 100644 --- a/test/skills-plugin-import.test.js +++ b/test/skills-plugin-import.test.js @@ -83,7 +83,8 @@ test("failed plugin sync preserves the last approved package", async () => { assert.equal(failed.ok, false); const after = getSkill("last-good-package"); assert.equal(after.deleted, false); - assert.equal(after.activeRevisionId, before.activeRevisionId); + assert.ok(before.currentRevisionId); + assert.equal(after.currentRevisionId, before.currentRevisionId); }); test("manifest edits regenerate catalog metadata and cannot disguise a plugin as a plain skill", async () => {