skills/orderly-plugin-write/SKILL.md
Use when the user wants to write / develop Orderly plugin code — including interceptors, hooks, lifecycle hooks, component patterns, and best practices. Triggers on "develop Orderly plugin", "write plugin", "add interceptor", "plugin architecture", "Orderly hooks", "plugin component", "Orderly SDK patterns".
npx skillsauth add orderlynetwork/orderly-skills orderly-plugin-writeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Write plugin code after scaffolding. Covers architecture, interceptor strategies, hooks usage, and best practices.
Direct SDK Package Usage
@orderly.network/hooks, @orderly.network/ui, @orderly.network/utils directly — same as in host apps.Only SDK-Declared Injectable Targets Can Be Intercepted
Trading.OrderEntry.SubmitButton) to intercept.Docs-First with Orderly docs MCP server
orderly_docs_search, orderly_docs_get_hook, orderly_docs_get_component, and orderly_docs_get_type to confirm signatures and usage.orderly_docs_get_component_doc, orderly_docs_get_workflow, or orderly_docs_get_recipe.| Type | Definition | Integration | Typical Use Cases |
|------|-----------|-------------|-------------------|
| Widget | UI component mounted to a specific anchor via interceptor | Declare target in interceptor, SDK auto-injects | PnL analyzer, quick close button, fee display, navigation bars |
| Page | Complete page built with SDK UI & Hooks, routed by host | Host adds via its own router (React Router, Next.js, etc.) | Asset overview, order history, leaderboard, settings |
| Layout | Trading page layout container (Desktop only) | Intercepts Trading.Layout.Desktop | Multi-column layout, sidebar, responsive toggle |
{ target, component } where component: (Original, props, api) => ReactNodeOrderlyPluginProvider + registerLayoutSplitPlugin()layoutStrategy={gridStrategy} + getInitialLayout={() => ...} on TradingPageregisterLayoutGridPlugin() from @orderly.network/layout-coreA plugin exports a registration function:
export function registerMyPlugin(options = {}) {
return (SDK: OrderlySDK) => {
SDK.registerPlugin({
id: "my-plugin",
name: "My Plugin",
version: "1.0.0",
orderlyVersion: ">=3.0.0",
interceptors: [
{ target: "...", component: (Original, props, api) => {...} },
],
setup: (api) => { /* event subscriptions */ },
onInitialize: () => { /* after code loads, before mounting */ },
onInstall: () => { /* first install */ },
onError: (error) => { /* error handling */ },
});
};
}
Key: The component function in an interceptor is a plain function (not a React component) — it cannot call Hooks directly. Return a wrapper component instead.
The interceptor function: component: (Original, props, api) => ReactNode
Render additional UI alongside Original:
{
target: "Trading.OrderEntry.SubmitButton",
component: (Original, props, api) => {
const BalanceWarning = () => {
const { balance } = useAccount();
return (
<div className="flex flex-col gap-2">
{balance < 100 && (
<div className="text-red-500 text-sm">Insufficient balance</div>
)}
<Original {...props} />
</div>
);
};
return <BalanceWarning />;
},
}
Wrap Original with a styled container or provider:
{
target: "OrderBook.Desktop.Asks",
component: (Original, props, api) => {
const CustomWrapper = () => (
<div className="border border-blue-500 rounded">
<div className="bg-blue-50 p-2 text-sm">Ask Side</div>
<Original {...props} />
</div>
);
return <CustomWrapper />;
},
}
Ignore Original and render custom component. Use with caution:
{
target: "Trading.OrderEntry.SubmitButton",
component: (Original, props, api) => {
const CustomSubmit = () => {
const { submit } = useOrderEntry();
return (
<button className="bg-green-500 text-white px-6 py-2 rounded" onClick={() => submit()}>
Execute Trade
</button>
);
};
return <CustomSubmit />;
},
}
Multiple plugins on the same target chain: Interceptor A -> Interceptor B -> Original
component: (Original, props, api) => {
const Wrapper = () => {
const { balance } = useAccount();
return <Original {...props} />;
};
return <Wrapper />;
}
componentcomponent: (Original, props, api) => {
const { balance } = useAccount(); // ❌ Breaks Rules of Hooks
return <div>{balance}</div>;
}
In setup() or lifecycle hooks, use the injected api object:
setup: (api) => {
api.events.on("place_order_success", (data) => {
console.log("Order placed", data);
});
api.events.on("deposit_success", (data) => {
console.log("Deposit successful", data);
});
}
createInterceptor (Recommended)import { createInterceptor } from "@orderly.network/plugin-core";
interceptors: [
createInterceptor("Deposit.DepositForm", (Original, props, api) => {
// props automatically typed
return <div onClick={props.onOk}>Custom Form</div>;
}),
]
import type { PluginInterceptor, DepositFormProps } from "@orderly.network/ui";
const interceptor: PluginInterceptor<DepositFormProps> = {
target: "Deposit.DepositForm",
component: (Original, props, api) => { /* props typed */ },
};
component: (Original, props: DepositFormProps, api) => { ... }
| Hook | Timing | Use Cases |
|------|--------|-----------|
| onInitialize | After code loads, before mounting | Initialize state, subscribe to events |
| onInstall | First time installed | Check SDK version, validate environment |
| onError | When interceptor throws | Custom error logging |
| onFallback | When fallback UI needed | Graceful degradation |
onInitialize: () => {
console.log("Plugin initializing...");
},
onInstall: () => {
if (state?.orderlyVersion && !isVersionCompatible(state.orderlyVersion)) {
throw new Error("SDK version incompatible");
}
},
onError: (error: Error) => {
console.error("Plugin error:", error);
},
PluginErrorBoundarycomponent functionsconst MyWrapper = React.memo(({ data, Original, props }) => {
return <Original {...props} />;
});
component: (Original, props, api) => (
<MyWrapper Original={Original} props={props} data={someData} />
)
Must match the Marketplace API wherever the plugin id appears (manifest pluginId, registerPlugin({ id }), CLI --id):
| Rule | Regex | Examples |
|------|--------|---------|
| Letter first; then letters, digits, hyphens only | /^[a-zA-Z][a-zA-Z0-9-]*$/ | my-plugin, orderly-onramp, pnlWidget |
Do not treat registerPlugin({ id }) as a separate camelCase-only rule — hyphenated ids are valid if they match the regex above.
export default function registerMyPlugin(options?: { theme?: string }) {
return (SDK: OrderlySDK, state?: { orderlyVersion?: string }) =>
SDK.registerPlugin({
id: "my-plugin",
name: "My Plugin",
version: "1.0.0",
onInitialize: () => {
console.log("Plugin initializing...");
},
onInstall: () => {
// Validate SDK version
if (
state?.orderlyVersion &&
!isVersionCompatible(state.orderlyVersion)
) {
throw new Error("SDK version incompatible");
}
},
onError: (error: Error) => {
console.error("Plugin error:", error);
// Send to error tracking service
},
interceptors: [
/* ... */
],
setup: (api) => {
/* ... */
},
});
}
Note: onMount, onUnmount, onDispose are not yet implemented in plugin-core.
reference.md — shared interceptor targets list (keep in sync with CLI constants)
tools
Prepare and submit / publish an Orderly plugin to the Marketplace via `orderly-devkit submit`. Covers README generation (with user consent), required usagePrompt drafting and approval, manifest updates, and API submission. Use when the user wants to publish, release, upload, or submit a plugin to the Orderly Marketplace. Triggers on "submit plugin", "publish plugin", "release plugin", "marketplace", "upload plugin", "usagePrompt", "orderly-devkit submit", "plugin manifest".
tools
Use when the user wants to scaffold / generate a new Orderly plugin project via the official `@orderly.network/cli` or `orderly-devkit` CLI. Triggers on "create Orderly plugin", "new Orderly plugin", "scaffold plugin", "generate plugin", "orderly-devkit create plugin".
tools
Add / integrate / register an Orderly plugin into an Orderly SDK DEX host app. Use when the user wants to install an Orderly plugin, wire a plugin into OrderlyAppProvider, or connect a local plugin package. Triggers on "add Orderly plugin", "install Orderly plugin", "integrate Orderly plugin".
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.