src — plugin-sdk

Module: src-plugin-sdk Cohesion: 0.80 Members: 0

src — plugin-sdk

The src/plugin-sdk module is the public-facing API for developing extensions for Code Buddy. It provides a set of type-safe helper functions and interfaces that simplify the creation of various plugin types: general-purpose plugins, channel integrations, LLM providers, and AI tools. Its primary goal is to offer a consistent, declarative, and robust developer experience for extending Code Buddy's capabilities.

This module acts as a facade, re-exporting and wrapping core internal types and functionalities from other Code Buddy modules (like ../plugins, ../channels, ../providers, ../context) into a developer-friendly SDK.

Module Structure

The plugin-sdk is organized into several sub-modules, each focusing on a specific aspect of plugin development:

graph TD
    subgraph Plugin SDK
        A[index.ts] --> B(core.ts)
        A --> C(channel.ts)
        A --> D(llm.ts)
        A --> E(tool.ts)
        A --> F(testing.ts)
    end

    subgraph Internal Code Buddy Modules
        G[../plugins/types.js]
        H[../channels/core.js]
        I[../providers/types.js]
        J[../context/context-engine.js]
    end

    B --> G
    C --> H
    D --> G
    D --> I
    F --> G
    F --> I
    F --> E
    F --> D
    A --> J

Figure 1: High-level dependencies within the Plugin SDK and to internal Code Buddy modules.

Core Plugin Development (src/plugin-sdk/core.ts)

This module provides the fundamental building blocks for any Code Buddy plugin.

Key Concepts

definePlugin Function

The definePlugin function is the primary entry point for defining a general-purpose plugin. It takes a DefinePluginConfig object, which bundles the manifest, optional lifecycle hooks, and the main activate and deactivate functions. This function handles the orchestration of lifecycle hooks around your core logic, ensuring a consistent activation and deactivation flow.

import { definePlugin } from '@phuetz/code-buddy/plugin-sdk/core';

export default definePlugin({
  manifest: {
    id: 'my-first-plugin',
    name: 'My First Plugin',
    version: '1.0.0',
    description: 'A simple example plugin.',
    permissions: ['read_config'],
  },
  lifecycle: {
    onBeforeActivate: async () => {
      console.log('Preparing to activate...');
      return true; class="hl-cmt">// Allow activation to proceed
    },
    onAfterActivate: () => {
      console.log('Plugin fully activated!');
    },
  },
  async activate(ctx) {
    ctx.logger.info('My First Plugin activated!');
    class="hl-cmt">// Access config: ctx.config.mySetting
    class="hl-cmt">// Register a tool: ctx.registerTool(...)
  },
  async deactivate() {
    console.log('My First Plugin deactivated.');
    class="hl-cmt">// Clean up resources
  },
});

Channel Plugins (src/plugin-sdk/channel.ts)

This module facilitates the creation of plugins that enable Code Buddy to communicate with new messaging platforms.

Key Concepts

defineChannel Function

The defineChannel function is a type-safe helper to create a ChannelPlugin instance. It takes a DefineChannelConfig object, which requires implementations for all ChannelPlugin methods.

import { defineChannel, ChannelStatus } from '@phuetz/code-buddy/plugin-sdk/channel';

let messageHandler: ((message: InboundMessage) => void) | undefined;
let isConnected = false;

const myWebChatChannel = defineChannel({
  type: 'webchat',
  async connect() {
    class="hl-cmt">// Simulate connection setup
    console.log('WebChat channel connecting...');
    await new Promise(resolve => setTimeout(resolve, 500));
    isConnected = true;
    console.log('WebChat channel connected.');
    class="hl-cmt">// In a real scenario, you'd set up a WebSocket server or similar
  },
  async disconnect() {
    console.log('WebChat channel disconnecting...');
    isConnected = false;
    class="hl-cmt">// Clean up server/connections
  },
  async send(message) {
    console.log(`Sending message to webchat: ${message.text}`);
    class="hl-cmt">// Simulate sending to connected clients
    return { success: true, timestamp: new Date() };
  },
  getStatus(): ChannelStatus {
    return { type: 'webchat', connected: isConnected, authenticated: true };
  },
  onMessage(handler) {
    messageHandler = handler;
    class="hl-cmt">// In a real scenario, wire up your incoming message events to call `handler`
    class="hl-cmt">// Example: simulate an incoming message after 2 seconds
    setTimeout(() => {
      if (messageHandler) {
        messageHandler({
          id: 'msg-123',
          channelId: 'webchat-general',
          sender: { id: 'user-abc', name: 'Web User' },
          text: 'Hello Code Buddy!',
          timestamp: new Date(),
          type: 'text',
        });
      }
    }, 2000);
  },
  isUserAllowed(userId: string): boolean {
    return userId.startsWith('user-'); class="hl-cmt">// Only allow users with 'user-' prefix
  },
});

LLM Provider Plugins (src/plugin-sdk/llm.ts)

This module provides the API for integrating new Large Language Models (LLMs) into Code Buddy, allowing the AI agent to use different model backends.

Key Concepts

defineLLMProvider Function

The defineLLMProvider function is a type-safe helper to create an LLMProviderPlugin. It requires an id, name, initialize, chat, and listModels implementation, along with optional shutdown, complete, and getModelInfo.

import { defineLLMProvider } from '@phuetz/code-buddy/plugin-sdk/llm';
import type { LLMMessage } from '@phuetz/code-buddy/providers/types'; class="hl-cmt">// Internal type

const myLLMProvider = defineLLMProvider({
  id: 'my-custom-llm',
  name: 'My Custom LLM Provider',
  priority: 10, class="hl-cmt">// Higher priority means preferred
  config: {
    apiKey: process.env.MY_LLM_API_KEY,
  },
  async initialize() {
    if (!this.config?.apiKey) {
      throw new Error('My LLM Provider requires an API key.');
    }
    console.log('My LLM Provider initialized.');
  },
  async shutdown() {
    console.log('My LLM Provider shutting down.');
  },
  async chat(messages: LLMMessage[], options) {
    console.log(`Chat request for model: ${options?.model || 'default'}`);
    class="hl-cmt">// Simulate API call to your LLM
    const lastMessage = messages[messages.length - 1];
    return `Echo: ${lastMessage.content}`;
  },
  async listModels() {
    return [
      { name: 'my-llm/fast-model', contextWindow: 8192, maxOutput: 1024 },
      { name: 'my-llm/large-model', contextWindow: 32768, maxOutput: 4096, supportsToolCalling: true },
    ];
  },
  async getModelInfo(modelName: string) {
    const models = await this.listModels();
    return models.find(m => m.name === modelName);
  },
});

Tool Plugins (src/plugin-sdk/tool.ts)

This module provides the API for defining tools that the AI agent can invoke to perform specific actions or retrieve information.

Key Concepts

defineToolPlugin Function

The defineToolPlugin function is a helper to define a tool plugin. It takes an id, name, and an array of ToolDefinitions. It also includes validation to ensure unique tool names within the plugin.

import { defineToolPlugin } from '@phuetz/code-buddy/plugin-sdk/tool';

export default defineToolPlugin({
  id: 'weather-tools',
  name: 'Weather Information Tools',
  tools: [
    {
      name: 'get_current_weather',
      description: 'Get the current weather conditions for a specified city.',
      parameters: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: 'The name of the city.',
          },
          unit: {
            type: 'string',
            enum: ['celsius', 'fahrenheit'],
            default: 'celsius',
            description: 'The unit of temperature.',
          },
        },
        required: ['city'],
      },
      readOnly: true,
      async execute({ city, unit }) {
        try {
          class="hl-cmt">// Simulate API call to a weather service
          const temperature = Math.floor(Math.random() * 15) + 15; class="hl-cmt">// 15-30°C
          const conditions = ['sunny', 'cloudy', 'rainy'][Math.floor(Math.random() * 3)];
          const tempUnit = unit === 'fahrenheit' ? 'F' : 'C';
          const displayTemp = unit === 'fahrenheit' ? (temperature * 9/5) + 32 : temperature;

          return {
            success: true,
            output: `The weather in ${city} is ${conditions} with a temperature of ${displayTemp}°${tempUnit}.`,
            metadata: { city, unit, temperature: displayTemp, conditions },
          };
        } catch (error: any) {
          return { success: false, error: `Failed to fetch weather: ${error.message}` };
        }
      },
    },
  ],
});

Advanced Topics

Context Engine Alignment (delegateCompactionToRuntime)

The delegateCompactionToRuntime() function is a sentinel, primarily for documentation and future runtime hooks. It signals to the Code Buddy runtime that a plugin's context engine (if it provides one) does not handle its own context compaction. This means the runtime should apply its built-in auto-compaction logic before calling the engine's assemble() method.

Plugins that provide custom context engines should set ownsCompaction = false (or omit it) and can use this function to explicitly state this intent. This aligns with the "Native Engine v2026.3.13-1" specification.

import { delegateCompactionToRuntime } from '@phuetz/code-buddy/plugin-sdk';
import type { ContextEngine, AssembleResult } from '@phuetz/code-buddy/plugin-sdk';

class MyCustomContextEngine implements ContextEngine {
  readonly ownsCompaction = delegateCompactionToRuntime().ownsCompaction; class="hl-cmt">// Explicitly delegate

  async assemble(messages: any[], tools: any[], maxTokens: number): Promise<AssembleResult> {
    class="hl-cmt">// The runtime will have already compacted `messages` if ownsCompaction is false
    class="hl-cmt">// Your logic here focuses on assembling the final prompt
    return {
      messages: messages,
      tokens: messages.length * 4, class="hl-cmt">// Simplified token count
      truncated: false,
    };
  }
}

Testing Utilities (src/plugin-sdk/testing.ts)

This module provides mock objects and assertion helpers for unit testing plugins without depending on the full Code Buddy runtime.

Key Components

        import { createMockPluginContext } from &#39;@phuetz/code-buddy/plugin-sdk/testing&#39;;
        import myToolPlugin from &#39;./my-tool-plugin&#39;; class="hl-cmt">// Your plugin

        describe(&#39;My Tool Plugin&#39;, () => {
          it(&#39;should register its tools correctly&#39;, async () => {
            const ctx = createMockPluginContext();
            await myToolPlugin.activate(ctx); class="hl-cmt">// Assuming tool plugins have an activate method or are directly registered

            expect(ctx.registeredTools).toHaveLength(1);
            expect(ctx.registeredTools[0].name).toBe(&#39;get_current_weather&#39;);
            expect(ctx.logger.messages).toContainEqual(
              expect.objectContaining({ level: &#39;info&#39;, message: &#39;Plugin activated!&#39; })
            );
          });
        });
        import { createMockLLMProvider } from &#39;@phuetz/code-buddy/plugin-sdk/testing&#39;;

        describe(&#39;Mock LLM Provider&#39;, () => {
          it(&#39;should return canned responses and track calls&#39;, async () => {
            const provider = createMockLLMProvider({
              responses: [&#39;First response&#39;, &#39;Second response&#39;],
            });

            const result1 = await provider.chat([{ role: &#39;user&#39;, content: &#39;Hi&#39; }]);
            expect(result1).toBe(&#39;First response&#39;);
            expect(provider.chatCalls).toHaveLength(1);

            const result2 = await provider.chat([{ role: &#39;user&#39;, content: &#39;How are you?&#39; }]);
            expect(result2).toBe(&#39;Second response&#39;);
            expect(provider.chatCalls).toHaveLength(2);
          });
        });
        import { assertToolResult } from &#39;@phuetz/code-buddy/plugin-sdk/testing&#39;;
        import { ToolResult } from &#39;@phuetz/code-buddy/plugin-sdk/tool&#39;;

        describe(&#39;assertToolResult&#39;, () => {
          it(&#39;should assert successful tool results&#39;, () => {
            const result: ToolResult = { success: true, output: &#39;Data fetched: 123&#39; };
            assertToolResult(result, { success: true, output: &#39;Data fetched&#39; });
          });

          it(&#39;should assert failed tool results&#39;, () => {
            const result: ToolResult = { success: false, error: &#39;Network error&#39; };
            expect(() => assertToolResult(result, { success: true })).toThrow(/Expected tool result success to be true, got false/);
            assertToolResult(result, { success: false, error: &#39;Network&#39; });
          });
        });

These testing utilities are crucial for maintaining the quality and reliability of plugins by enabling isolated and efficient unit testing.