Soubi

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

Install only soubi. Internal architecture is deliberately not exposed as a catch-all API:

Public importRole
soubiThe define* and file() authoring helpers plus the init · build · check · install · dev CLI.
soubi/compilerThe 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.

On this page