Skip to content

Commit

Permalink
feat: add plugin generator script (#227)
Browse files Browse the repository at this point in the history
* feat: add plugin generator script

Added a new script that generates GOAT SDK plugins with:
- Configurable plugin name and chain type (evm/solana/any)
- Standard package structure and configuration
- Proper dependency management
- TypeScript configuration
- Example tool implementation

The script can be run using: pnpm create-plugin -n plugin-name -t chain-type

Co-Authored-By: Agus Armellini Fischer <[email protected]>

* feat: enhance plugin generator
- Add standard package.json metadata fields
- Simplify tsconfig.json inheritance
- Make 'any' the default plugin type

Co-Authored-By: Agus Armellini Fischer <[email protected]>

* fix: resolve lint issues with type casting and module config

Co-Authored-By: Agus Armellini Fischer <[email protected]>

* chore: remove test plugin package

Co-Authored-By: Agus Armellini Fischer <[email protected]>

* docs: add create-plugin command instructions to plugins documentation

Co-Authored-By: Agus Armellini Fischer <[email protected]>

* Update plugins.mdx

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Agus Armellini Fischer <[email protected]>
Co-authored-by: Agus <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent 3cbe996 commit cd74a87
Show file tree
Hide file tree
Showing 6 changed files with 700 additions and 52 deletions.
121 changes: 74 additions & 47 deletions docs/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,34 @@ This allows plugins to:
----

## Creating your own GOAT plugin
Building a custom GOAT plugin is straightforward. Below, we'll walk you through creating a plugin that signs messages with "BAAAA" 🐐 prefixed to them.

### Using the Plugin Generator

The easiest way to create a new plugin is using the `create-plugin` command. This command generates all the necessary files and configuration for a new plugin.

```bash
# Create a plugin with default type (any)
pnpm create-plugin -n your-plugin-name

# Create a plugin for a specific chain type
pnpm create-plugin -n your-plugin-name -t evm # For EVM chains
pnpm create-plugin -n your-plugin-name -t solana # For Solana
```

The command will generate:
- A package.json with standard metadata and dependencies
- TypeScript configuration files (tsconfig.json, tsup.config.ts)
- A basic plugin structure in the src directory:
- parameters.ts - Example parameters using Zod schema
- your-plugin-name.service.ts - Service class with an example tool
- your-plugin-name.plugin.ts - Plugin class extending PluginBase
- index.ts - Exports for your plugin

After generating the plugin, you can customize the generated code according to your needs following the guide below.

### Manual Creation (Example)

For a detailed understanding of plugin structure, let's walk through creating a plugin that signs messages with "BAAAA" 🐐 prefixed to them.

<Steps>
<Step title="Define the plugin interface">
Expand Down Expand Up @@ -79,10 +106,53 @@ export const baaaSigner = () => new BAAAASigner();
</Step>
<Step title="Add tools to the plugin">
There are two ways to add tools to the plugin:
1. Using the `getTools` and `createTool` functions
2. Using the `@Tool` decorator on our own class
1. Using the `@Tool` decorator on our own class
2. Using the `getTools` and `createTool` functions to create tools dynamically

#### Option 1: Using the `@Tool` decorator
The `@Tool` decorator is a way to create tools in a more declarative way.
You can create a class and decorate its methods with the `@Tool` decorator to create tools.
The tool methods will receive the wallet client as the first argument and the parameters as the second argument.

```typescript
import { Tool } from "@goat-sdk/core";

#### Option 1: Using the `getTools` and `createTool` functions
class MyTools {
@Tool({
name: "sign_message_baaaa",
description: "Sign a message with 'BAAAA' prefix",
parameters: z.object({
message: z.string(),
}),
})
async signMessage(walletClient: WalletClientBase, parameters: z.infer<typeof parameters>) {
const originalMessage: string = parameters.message;
const prefixedMessage = `BAAAA${originalMessage}`;
const signed = await walletClient.signMessage(prefixedMessage);
return signed.signedMessage;
}
}
```

Once we have our class we now need to import it in our plugin class.

```typescript
import { PluginBase, WalletClientBase } from "@goat-sdk/core";
import { MyTools } from "./my-tools.service.ts";

export class BAAAASigner extends PluginBase<WalletClientBase> {
constructor() {
// Import the tools we created in the previous step here
super("baaaSigner", [new MyTools()]);
}

supportsChain = (chain: Chain) => true;
}

export const baaaSigner = () => new BAAAASigner();
```

#### Option 2: Using the `getTools` and `createTool` functions
We will start by implementing the `getTools` method in our plugin class.

Inside the method, we will return an array of tools created using the `createTool` function.
Expand Down Expand Up @@ -124,49 +194,6 @@ export class BAAAASigner extends PluginBase<WalletClientBase> {
}

// We export a factory function to create a new instance of the plugin
export const baaaSigner = () => new BAAAASigner();
```
#### Option 2: Using the `@Tool` decorator
The `@Tool` decorator is a way to create tools in a more declarative way.
You can create a class and decorate its methods with the `@Tool` decorator to create tools.
The tool methods will receive the wallet client as the first argument and the parameters as the second argument.
```typescript
import { Tool } from "@goat-sdk/core";
class MyTools {
@Tool({
name: "sign_message_baaaa",
description: "Sign a message with 'BAAAA' prefix",
parameters: z.object({
message: z.string(),
}),
})
async signMessage(walletClient: WalletClientBase, parameters: z.infer<typeof parameters>) {
const originalMessage: string = parameters.message;
const prefixedMessage = `BAAAA${originalMessage}`;
const signed = await walletClient.signMessage(prefixedMessage);
return signed.signedMessage;
}
}
```

Once we have our class we now need to import it in our plugin class.

```typescript
import { PluginBase, WalletClientBase } from "@goat-sdk/core";
import { MyTools } from "./my-tools.service.ts";

export class BAAAASigner extends PluginBase<WalletClientBase> {
constructor() {
// Import the tools we created in the previous step here
super("baaaSigner", [new MyTools()]);
}

supportsChain = (chain: Chain) => true;
}

export const baaaSigner = () => new BAAAASigner();
```
</Step>
Expand Down
7 changes: 6 additions & 1 deletion typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"version": "0.1.1",
"scripts": {
"prepare": "cd .. && pnpm install && cd typescript",
"create-plugin": "tsx scripts/createPlugin.ts",
"build": "pnpm turbo build",
"build:libs": "pnpm turbo --filter \"./packages/**\" build",
"build:libs:prod": "cross-env NODE_ENV=production pnpm build:libs",
Expand All @@ -27,6 +28,7 @@
"ts-node": "10.9.2",
"tsc-alias": "1.8.10",
"tsup": "8.3.5",
"tsx": "4.19.2",
"turbo": "2.3.1",
"typescript": "5.6.3",
"vitest": "2.1.5"
Expand All @@ -47,5 +49,8 @@
"url": "https://github.com/crossmint/goat/issues"
},
"keywords": ["ai", "agents", "web3"],
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"dependencies": {
"commander": "13.0.0"
}
}
Loading

0 comments on commit cd74a87

Please sign in to comment.