Plugin Development
Create post-processing plugins for CyanPrint templates
Plugin Development
Plugins run after file generation, enabling post-processing like running commands or modifying files. While processors transform file content, plugins perform operations on the generated project.
What is a Plugin?
A plugin is a Docker container that:
- Receives directory containing all generated files
- Runs operations on the directory (commands, file modifications)
- Returns directory path for the next step
Plugins are the final step in the generation pipeline - they run after all processors have completed.
Plugin Architecture
Components
| Component | Purpose | Examples |
|---|---|---|
| Entry Point | Plugin logic | StartPluginWithLambda |
| CyanPluginInput | Input from system | Directory, config |
| PluginOutput | Return value | Directory path |
| Shell/Bun | Command execution | $ from bun, exec |
What Plugins Can Do
Plugins have full access to the generated project directory:
| Capability | Example |
|---|---|
| Initialize git | git init, git add . |
| Install dependencies | npm install, bun install |
| Run formatters | prettier --write ., biome format . |
| Set up hooks | husky install |
| Create symlinks | Link config files |
| Build steps | npm run build |
| File modifications | Edit generated files |
Plugins are the only component that can execute shell commands. Processors are stateless file transformers only.
Learning Path
Start Here: Tutorials
Build your first plugin step by step:
How-To Guides
Task-oriented guides for common scenarios:
Run Shell Commands
Execute commands in the directory
Modify Generated Files
Read and edit files
Conditional Execution
Logic based on config
Push to Registry
Publish your plugin
Reference
Technical documentation for the SDK:
- StartPluginWithLambda - Entry point
- CyanPluginInput/PluginOutput - Interfaces
- Type Definitions - Full type reference
- Plugin Dockerfile - Container setup
Explanation
Deep dives into plugin concepts:
- What Are Plugins - Purpose and use cases
- Plugins vs Processors - When to use each
- Execution Order - Pipeline sequence
Quick Example
Here's a minimal plugin that initializes git and installs dependencies:
import { StartPluginWithLambda } from '@atomicloud/cyan-sdk';import { $ } from 'bun';StartPluginWithLambda(async (input) => {const { directory, config } = input;const cfg = config as { installDeps?: boolean };// Initialize git repositoryawait $`git -C ${directory} init`.quiet();// Optionally install dependenciesif (cfg.installDeps) {await $`cd ${directory} && npm install`.quiet();}return { directory };});
import asyncioimport subprocessfrom cyanprintsdk.plugin import start_plugin_with_fnfrom cyanprintsdk.protocol import PluginInput, PluginOutputasync def run_plugin(input: PluginInput) -> PluginOutput:cfg = input.config or {}directory = input.directory# Initialize git repositorysubprocess.run(['git', 'init'], cwd=directory, capture_output=True)# Optionally install dependenciesif cfg.get('installDeps'):subprocess.run(['npm', 'install'], cwd=directory, capture_output=True)return PluginOutput(directory=directory)plugin_main = start_plugin_with_fn(run_plugin)
using CyanPrintSDK;using System.Diagnostics;public static class Plugin{[PluginMain]public static async Task<PluginOutput> Run(PluginInput input){var cfg = input.Config;var directory = input.Directory;// Initialize git repositoryawait RunCommand("git", "init", directory);// Optionally install dependenciesif (cfg.TryGetValue("installDeps", out var installDeps) && installDeps == "true"){await RunCommand("npm", "install", directory);}return new PluginOutput { Directory = directory };}private static Task RunCommand(string cmd, string args, string cwd){var psi = new ProcessStartInfo(cmd, args) { WorkingDirectory = cwd };Process.Start(psi)?.WaitForExit();return Task.CompletedTask;}}