Architecture
How the single Soubi package is organized and how a PortablePlugin flows from disk to native artifacts.
Soubi has one normalized plugin model and explicit boundaries for discovery, loading, compilation, harness emission, and installation.
Compilation pipeline
Build stays inspectable
soubi build writes only to .soubi-out. Installing into a repository or a
global harness location is a separate, ownership-aware operation.
What happens at each boundary
Discover the plugin directory
Discovery recognizes paths such as AGENTS.md, skills/*/SKILL.md, commands/, agents/, hooks/, tools.ts, mcp.ts, and assets/. It does not import authored modules and reports ambiguous singleton sources with stable diagnostic codes.
Load and normalize one PortablePlugin
The loader imports the discovered TypeScript definitions and combines Markdown, frontmatter, referenced files, and bundled tools into one in-memory object. Every requested harness receives this same model.
Select target adapters
The compiler resolves targets from --harness, plugin.config.ts, or the built-in harness descriptors. Each target is compiled independently.
Emit native files and diagnostics
The adapter uses core capability helpers to decide whether each feature is supported, translated, dropped, or a hard failure. It returns files, warnings, and failures without writing to disk.
Validate and write artifacts
The compiler normalizes every output path, rejects duplicate normalized destinations, excludes only targets with failures, and writes successful outputs beneath .soubi-out/<target>/.
Package boundaries
Core
Portable types, capability data, emission helpers, and frontmatter. This source folder has no runtime dependencies.
Discovery
Source classification, stable diagnostics, authored-module loading, and normalization.
Compiler
Target selection, adapter orchestration, path validation, warnings, and per-target exclusion.
Adapters
Twenty-two emitters that translate the portable model into native layouts and configuration.
Install only soubi. Internal architecture is deliberately not exposed as a catch-all API:
| Public import | Role |
|---|---|
soubi | The define* and file() authoring helpers plus the init · build · check · install · dev CLI. |
soubi/compiler | The focused compileProject() programmatic build API and result types. |
The adapter contract
Every adapter implements one method and returns data:
interface HarnessAdapter {
id: HarnessId;
displayName: string;
emit(plugin: PortablePlugin): EmitResult;
}
interface EmitResult {
files: OutputFile[];
warnings: BuildWarning[];
failures: BuildFailure[];
}Failures are target-local
A non-empty failures array excludes that adapter's output. Warnings record
degrade and drop decisions but never block the other targets.
Static output and generated runtime
Most adapters emit Markdown, JSON, TOML, or YAML that the harness reads directly. The output has no dependency on Soubi and can be inspected before installation.
Harnesses with plugin SDKs receive generated runtime code. Portable tools are bundled into self-contained ESM and execute with Node; generated targets do not fetch tsx at runtime.
- OpenCode and Kilo receive TypeScript plugin modules.
- Pi, OMP, and Amp receive TypeScript extensions.
- Hermes receives a Python plugin.
- Maki receives a Lua plugin.
The generated source is written beside the rest of that harness's artifacts.
See Add an Adapter to extend the pipeline with another target.