Plugins are how you package and share reusable Aiki behaviors — custom hooks, templates, or both. A plugin is a GitHub repo with a specific directory structure.
A plugin is a GitHub repository containing:
plugin.yaml— plugin metadata (name, description) — requiredhooks.yaml— flow definitions (event handlers)tasks/— markdown templates for tasks (plans, reviews, etc.)
A plugin.yaml manifest is required. At least one of hooks.yaml or tasks/ must also exist.
Minimal hooks-only plugin:
my-plugin/
├── plugin.yaml
└── hooks.yaml
Minimal tasks-only plugin:
my-plugin/
├── plugin.yaml
└── tasks/
└── review.md
Full plugin:
my-plugin/
├── plugin.yaml
├── hooks.yaml
└── tasks/
├── plan.md
└── review.md
Plugins use a namespace/name format:
- The namespace is your GitHub username or organization
- The name is the repository name
Examples: somecorp/security, myuser/style-checks
When referencing plugin templates, use three-part paths: namespace/name/template. For example, somecorp/security/review refers to the tasks/review.md file in the somecorp/security plugin.
The aiki namespace is reserved and maps to the glasner GitHub organization. Don't use it for your own plugins. Built-in flows like aiki/core and aiki/default use this namespace.
Here's a plugin that blocks dangerous shell commands:
# hooks.yaml
name: "Safety Guard"
description: "Block dangerous shell commands during AI sessions"
version: "1"
shell.permission_asked:
- if: event.command starts_with "rm -rf /"
then:
- block: "Blocked dangerous command: {{event.command}}"
- if: event.command starts_with "git push --force"
then:
- block: "Force-push blocked. Use regular git push."Users include it in their project:
# .aiki/hooks.yml
include:
- aiki/default
- myuser/safety-guardEvery plugin requires a plugin.yaml file at the root. It contains metadata used for discovery and display:
# plugin.yaml
name: My Plugin
description: One-line summary of what this plugin does| Field | Required | Purpose |
|---|---|---|
name |
Recommended | Human-readable display name. Falls back to {namespace}/{plugin} path if omitted. |
description |
Recommended | One-line summary of what the plugin does |
The name field is a display name, not an identifier. The machine identifier is always the {namespace}/{plugin} path (e.g., eslint/standard).
Fallback chain:
plugin.yamlname:fieldhooks.yamlname:field (standalone hook files with noplugin.yaml){namespace}/{plugin}path as display name
For plugins with both files, plugin.yaml takes precedence.
Templates are markdown files that define task structure. They can use {{variable}} interpolation and {{> partial}} includes.
Example: a custom review template:
---
assignee: claude-code
---
# Code Review
Review the changes for:
- Security vulnerabilities
- Performance issues
- Test coverage gaps
{{> review/criteria/code}}
Report findings as task comments.The {{> review/criteria/code}} syntax includes a partial from another plugin's templates.
Plugin dependencies are auto-derived from content.
Aiki scans your hooks.yaml and tasks/ for references to other plugins:
include:directives in hooks.yamlhook:actions referencing other plugins{{> namespace/plugin/template}}partials in task templates- Template references in spawn configs
References inside fenced code blocks (```) are excluded from dependency scanning.
Before publishing, test your plugin by symlinking it into the plugins directory:
# Create the namespace directory
mkdir -p ~/.aiki/plugins/myuser
# Symlink your plugin
ln -s /path/to/my-plugin ~/.aiki/plugins/myuser/my-pluginThen include it in a test project's .aiki/hooks.yml and verify the behavior.
Alternatively, use project-level overrides — place files directly in .aiki/tasks/myuser/my-plugin/ to test template changes without a symlink.
Push your plugin to GitHub:
cd my-plugin
git init && git add . && git commit -m "Initial plugin"
gh repo create myuser/my-plugin --public --source=. --pushUsers install it with:
aiki plugin install myuser/my-plugin# Install a specific plugin (and its dependencies)
aiki plugin install myuser/my-plugin
# Install all plugins referenced by the current project
aiki plugin install
# Update a specific plugin
aiki plugin update myuser/my-plugin
# Update all installed plugins
aiki plugin update
# List plugins and their status
aiki plugin list
# Remove a plugin
aiki plugin remove myuser/my-pluginPlugins can build on each other:
Pull in another plugin's handlers for all events:
# hooks.yaml
include:
- somecorp/base-hooksRun handlers before or after the main flow:
# hooks.yaml
before:
include:
- somecorp/pre-checks
after:
include:
- somecorp/post-validationBefore/after blocks can also define inline event handlers:
before:
session.started:
- context: "Pre-check: verify environment"Invoke another plugin's handler for the current event from within a handler:
change.completed:
- hook: "somecorp/formatter"
- log: "Formatting applied"- Keep plugins small and focused — one plugin per concern (security, formatting, CI integration)
- Document with comments — add YAML comments explaining what each handler does
- Avoid side effects in
permission_askedhandlers — they gate editor operations; keep them fast and deterministic - Use
on_failure: continuefor non-critical actions — don't block the user's workflow over optional features - Test with multiple editors — behavior may vary across Claude Code, Cursor, and Codex