# `run-plugin` ## Purpose End-to-end test harness for plugins. `runPlugin(plugin, options)` wires up an in-memory filesystem, a scripted CLI, a no-op logger, and a basic template engine, then drives the plugin through the full `detect → prompts → plan → apply → postInstall` pipeline. Returns the filesystem and the recorded CLI interaction so tests can assert on what was written and what was prompted. Zero real disk I/O. ## Features - Sensible defaults: in-memory `MemoryFs`, scripted `ScriptCli`, no-op logger, `{{var}}`-style template engine — all overridable. - Returns `{ ops, summary, handle, dryRun, fs, cli }` so tests can inspect the plan, the resulting fs, and the prompt history. - Same `RunOptions` surface as `Host.run`: `dryRun`, `force`, `noInteractive`, `only`, `exclude`, `signal`. - Defaults `noInteractive` to `true` so test runs never block on a prompt that wasn't scripted. ## Basic Example ```ts import { runPlugin } from "@suaveplan/installer"; const result = await runPlugin(myPlugin, { answers: { targetDir: "/.suaveplan/skills" }, }); expect(result.summary.written).toBe(5); ``` ## Advanced Example ```ts import { runPlugin, ScriptCli } from "@suaveplan/installer"; const cli = new ScriptCli({ confirm: [true, false] }); const ac = new AbortController(); const result = await runPlugin(myPlugin, { cli, signal: ac.signal, answers: { skills: ["echo", "crypto"] }, only: ["crypto"], force: false, }); expect(cli.record.confirms).toEqual([ { message: "Overwrite existing file?", answer: true }, ]); expect(result.ops.every((op) => op.path.includes("crypto"))).toBe(true); ``` ## API Reference - `runPlugin(plugin, options): Promise>` - `interface RunPluginOptions` — `{ answers?, fs?, cli?, cwd?, logger?, templates?, signal?, dryRun?, force?, noInteractive?, only?, exclude? }`. - `interface RunPluginResult extends HostRunResult` — adds `fs: MemoryFs` and `cli: InstallerCli`. ## Implementation Notes The default template engine handles only `{{varName}}` substitution and returns empty string for unknown vars — that's enough for plugin tests that don't care about templates and avoids adding a dependency on `@suaveplan/templates` to the test surface. The default `cwd` is `/` (POSIX root in `MemoryFs`'s namespace) so paths in plans can be written literally. Tests that need a different `cwd` should pass it explicitly; both `DiskFs` and `MemoryFs` honour the value identically.