Building Your First Clip
A Clip is a TypeScript package built with @pinixai/core. It defines commands that Agents (and humans) can invoke through the Hub.
Prerequisites
Section titled “Prerequisites”- Pinix installed and running
- Bun (installed automatically with Pinix)
Scaffold a new Clip
Section titled “Scaffold a new Clip”-
Create the project
Terminal window mkdir my-clip && cd my-clipbun init -ybun add @pinixai/core -
Create
clip.json{"name": "@yourscope/my-clip","version": "0.1.0","description": "My first Clip","main": "src/index.ts"} -
Write the Clip
Create
src/index.ts:import { Clip, command } from "@pinixai/core";const clip = new Clip({name: "@yourscope/my-clip",description: "My first Clip",});clip.command("hello", {description: "Say hello",input: {name: { type: "string", description: "Name to greet", required: true },},handler: async ({ input }) => {return { message: `Hello, ${input.name}!` };},});clip.run(); -
Test locally
Terminal window # Install as a local Clippinix hub add local/my-clip --path .# Invoke itpinix invoke my-clip hello --name "World"# → { "message": "Hello, World!" } -
Iterate
Edit
src/index.ts, the Runtime auto-restarts the Clip process. Invoke again to test changes.
Clip structure
Section titled “Clip structure”A minimal Clip project:
my-clip/├── clip.json # Package manifest├── src/│ └── index.ts # Entry point├── package.json # Dependencies└── web/ # Optional: Web UI └── index.htmlclip.json fields
Section titled “clip.json fields”| Field | Required | Description |
|---|---|---|
name | Yes | Package identifier: @scope/name |
version | Yes | Semver version |
description | Yes | What this Clip does |
main | Yes | Entry point relative to package root |
dependencies | No | Clip-to-Clip dependencies (slots) |
Adding more commands
Section titled “Adding more commands”clip.command("list", { description: "List all items", handler: async () => { const items = await db.getAll(); return { items, total: items.length }; },});
clip.command("add", { description: "Add a new item", input: { title: { type: "string", required: true }, priority: { type: "string", enum: ["low", "medium", "high"] }, }, handler: async ({ input }) => { const item = await db.create(input); return item; },});Cross-Clip invocation
Section titled “Cross-Clip invocation”Your Clip can invoke other Clips through the Hub:
import { invoke } from "@pinixai/core";
clip.command("research", { description: "Research a topic using search and browser", input: { topic: { type: "string", required: true }, }, handler: async ({ input }) => { // Call another Clip const results = await invoke("google", "search", { query: input.topic, }); return { results }; },});The invoke() function sends the request through the Hub, which routes it to the target Clip. Your Clip doesn’t need to know where the target Clip is running.
Dependency Binding
Section titled “Dependency Binding”If your Clip depends on other Clips (declared in dependencies in clip.json), use the bind command to point each dependency to a specific Clip instance:
# 查看当前绑定pinix hub bindings my-clip
# 绑定依赖:将 "browser" slot 绑定到 "browser" aliaspinix hub bind my-clip browser browser
# 解除绑定pinix hub unbind my-clip browser
# 列出满足某个 slot 依赖的已安装 Clippinix hub bind my-clip browser --listBinding information is stored on the Clip side (bindings.json), not in the Hub.
Adding a Web UI
Section titled “Adding a Web UI”Create a web/ directory in your Clip project with an index.html:
<!DOCTYPE html><html><head> <title>My Clip</title> <script type="module"> import { createClient } from "@pinixai/core/web"; const client = createClient(); // Use client.invoke() to call your Clip's commands </script></head><body> <!-- Your UI --></body></html>The Web UI is served by the Hub and accessible through the Console at /clips/<alias>/web.
Next steps
Section titled “Next steps”- Design Principles — how to design Clips for Agent consumption
- Command Design — detailed command design patterns
- Publishing — publish to the Registry