v0.4.0 — Architecture Re-shape + Hardening
v0.4.0 — Architecture Re-shape + Hardening
Architecture
@agentplugins/contract— new package: zod schema as single source of truth for the plugin manifest; TS types derived viaz.infer;manifest.schema.jsongenerated at build time viazod-to-json-schema. Eliminates the 4-copy drift class (previously:spec/v1/manifest.schema.json,packages/schema/src/types.ts,packages/core/src/types.ts, and inline zod).@agentplugins/compile— new shared codegen kernel with secure emit primitives (emitCommandHandler,emitInlineHandler,sanitizeName,sanitizeJoin,mapHook). All adapters route through this kernel — a capability now ships by editing one schema field + one kernel path, not 8 files.corefacade —@agentplugins/core/src/index.tsre-exports fromcontract/compile; public API unchanged; sibling repos (agentplugins-{goal,btw,flow,caveman,ponytail}) unaffected. (Store isolation into its own@agentplugins/storepackage is planned for v0.5.0.)ARCHITECTURE.mdat repo root documents the ports-&-adapters hexagonal compiler pipeline with mermaid diagrams: package dependency graph, compile pipeline (manifest→validate→IR→emit→files), distribute pipeline (source→fetch→install→link), and the adapter port contract.
Authoring primitives
continueWith— chained prompt injection via thestophook. OpenCode adapter usesctx.session.sendMessage; PiMono adapter usespi.sendUserMessage. Per-session iteration capped at 20 by default; lint rule (continueWithSafetyRule) inspects inline handler source at build time.nativeEntry/nativeCopies— native artifact passthrough for non-JS plugin artifacts. Build command resolves and writesnativeCopiesentries; adapters expose them in their output.adapterOverrides— runtime adapter override viamanifest.adapterOverrides.opencode/manifest.adapterOverrides.pimono. Paths are sanitized against the plugin root at compile time; a runtime trust warning is emitted.capabilities: ['subprocess']— gates the child-process lint block. ThehandlerSafetyRulelint rule scanscommandhandler strings for subprocess patterns; the gate only fires when the capability is absent.- Inline handlers auto-wrapped as command scripts for Codex and Kimi adapters.
Multi-artifact linking
- Multi-artifact
distlinking for OpenCode: multiple compiled artifacts per plugin. - Per-skill flat linking for Pi / Skills.sh compatibility.
Security hardening
| Class | Fix |
|---|---|
| RCE (template-literal injection) | emitCommandHandler uses plain string + arg-array execSync; no shell template literals, no shell:true. Structurally eliminated — not patched per adapter. |
| Path traversal — plugin name | sanitizeName() rejects ../absolute/non-kebab at addPluginFromSource install time. |
Path traversal — nativeCopies from/to |
sanitizeJoin() validates both src and dst paths resolve inside the plugin root in build command. |
| Path traversal — SKILL.md frontmatter | sanitizeName() applied to name: field from SKILL.md frontmatter before any symlink/write. |
Path traversal — adapterOverrides / handler source |
sanitizeJoin(pluginRoot, path) applied at compile time; runtime trust warning emitted. |
| SSRF bypass | safe-fetch re-validates every redirect hop against allow-list + private-IP check. |
| Arbitrary script execution at install | evaluateScriptPolicy() runs on add / import --write; denylisted lifecycle scripts block the install. |
| Integrity never verified | verifyIntegrity() called on install when manifest.integrity is declared. |
| Git clone URL injection | cloneRepo validates https://github.com/... before URL interpolation. |
| Symlink clobber | unlinkSync only; rmSync never on user dirs; lstatSync guard. |
| Install idempotency | unlinkAll() factors pre-delete for compiled + skills + native + symlink artifacts; stale links never left behind. |
| Silent partial installs | Symlink failures collected, warned at summary, process.exitCode=1. |
Bug fixes
subagentStopcollision fixed; deadagentCommand/agentCwdpassthrough removed.sidecarmarked experimental (x-experimental) in schema and docs.recompile-installeddogfood script moved fromcli/src/commands/→scripts/.- Two OpenCode test failures (stale
Bun.$assertions) corrected toexecSync (curl). - Example logger
package.jsonno longer carries a version field in its internal manifest. - CLI version read from
package.jsoninstead of hardcoded string.
Breaking changes
spawnChildremoved from@agentplugins/compileand@agentplugins/core. It had zero callers; no adapter used it. Any external consumer importing it directly will break — the replacement is to usenode:child_process.spawndirectly with sandboxing.@agentplugins/contractis a new published package. Its API (PluginManifestzod schema, derived types,manifest.schema.json) is stable from this release.corere-exports remain the preferred consumer entry.
Release notes backfilled for informational purposes; sourced from CHANGELOG.md.
Installation
npx @agentplugins/cli add user/your-plugin