Skip to content

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.

  1. Create the project

    Terminal window
    mkdir my-clip && cd my-clip
    bun init -y
    bun add @pinixai/core
  2. Create clip.json

    {
    "name": "@yourscope/my-clip",
    "version": "0.1.0",
    "description": "My first Clip",
    "main": "src/index.ts"
    }
  3. 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();
  4. Test locally

    Terminal window
    # Install as a local Clip
    pinix hub add local/my-clip --path .
    # Invoke it
    pinix invoke my-clip hello --name "World"
    # → { "message": "Hello, World!" }
  5. Iterate

    Edit src/index.ts, the Runtime auto-restarts the Clip process. Invoke again to test changes.

A minimal Clip project:

my-clip/
├── clip.json # Package manifest
├── src/
│ └── index.ts # Entry point
├── package.json # Dependencies
└── web/ # Optional: Web UI
└── index.html
FieldRequiredDescription
nameYesPackage identifier: @scope/name
versionYesSemver version
descriptionYesWhat this Clip does
mainYesEntry point relative to package root
dependenciesNoClip-to-Clip dependencies (slots)
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;
},
});

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.

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:

Terminal window
# 查看当前绑定
pinix hub bindings my-clip
# 绑定依赖:将 "browser" slot 绑定到 "browser" alias
pinix hub bind my-clip browser browser
# 解除绑定
pinix hub unbind my-clip browser
# 列出满足某个 slot 依赖的已安装 Clip
pinix hub bind my-clip browser --list

Binding information is stored on the Clip side (bindings.json), not in the Hub.

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.