Soubi

Assets

Ship references, scripts, configuration, and generated files with a portable plugin.

Assets are non-feature files that travel with a plugin: reference documents, scripts, JSON configuration, templates, and other files the emitted instructions or runtime needs.

Add skill resources

Files beside a SKILL.md are collected automatically:

skills/
└── dependency-audit/
    ├── SKILL.md
    ├── policy.json
    └── scripts/
        └── scan.mjs

Soubi preserves their relative paths. Harnesses without skill-adjacent resources report a fallback instead of silently discarding them.

Attach files to a feature

Commands, TypeScript skills, and TypeScript agents accept an assets array:

skills/audit.ts
import { defineSkill, file } from "soubi";

export default defineSkill({
  instructions: file("./audit.md"),
  assets: ["./policy.json", "./scripts/scan.mjs"],
});

Paths resolve relative to the module that declares them.

Add plugin-wide assets

Files under assets/ are collected recursively. You can also list individual paths in plugin.config.ts:

plugin.config.ts
import { defineConfig } from "soubi";

export default defineConfig({
  id: "dependency-audit",
  assets: ["NOTICE.md"],
});

Load a directory

Use readDir when files live elsewhere or need a destination prefix:

plugin.config.ts
import path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, readDir } from "soubi";

const here = path.dirname(fileURLToPath(import.meta.url));

export default defineConfig({
  id: "dependency-audit",
  files: readDir(path.join(here, "scripts"), {
    prefix: "scripts",
  }),
});

readDir preserves executable bits. Its exclude option skips entries by name.

Keep destination paths portable

Every destination must be relative and use ordinary path segments. Absolute paths, empty segments, and . or .. traversal are rejected before Soubi writes output.

Use soubi check to see where a target cannot carry an asset, then inspect the generated tree before installation.

On this page