npm package: @openpets/slack
Description: Slack integration plugin for OpenCode - search channels, read messages, and post to Slack
Latest version: 1.0.4

# Factory-Based Plugin Template

A comprehensive template for creating OpenCode plugins using the factory pattern with multi-provider workflow management. This template demonstrates modern plugin architecture with provider abstraction, configuration management, and workflow orchestration.

## Table of Contents

1. [Factory Architecture](#factory-architecture)
2. [Multi-Provider Design](#multi-provider-design)
3. [Quick Start](#quick-start)
4. [Configuration Management](#configuration-management)
5. [Workflow Tools](#workflow-tools)
6. [Provider Integration](#provider-integration)
7. [Best Practices](#best-practices)
8. [Migration Guide](#migration-guide)
9. [Testing](#testing)

---

## Factory Architecture

This template uses the factory pattern from `src/core/plugin-factory.ts`:

### Key Components

- **Factory Functions**: `createPlugin()` and `createSingleTool()`
- **Zod Schemas**: Type-safe parameter validation
- **Provider Abstraction**: Dynamic provider loading and fallbacks
- **Configuration Management**: Environment-based multi-provider setup

### Architecture Benefits

✅ **Type Safety**: Zod schemas ensure parameter validation  
✅ **Provider Agnostic**: Support multiple providers per service type  
✅ **Graceful Fallbacks**: Automatic provider switching on failures  
✅ **Configuration Driven**: Environment-based provider selection  
✅ **Workflow Focused**: High-level orchestration tools  

---

## Multi-Provider Design

### Provider Types

```typescript
interface PluginConfig {
  providers: {
    github?: { token: string; apiUrl?: string }
    maps?: { provider: 'googlemaps' | 'mapbox'; apiKey: string }
    ai?: { provider: 'fal'; apiKey: string }
  }
  preferences: Record<string, string>
  workflow: { timeout: number; retries: number; logLevel: string }
}
```

### Provider Selection Logic

1. **Configuration Loading**: Parse `PROVIDER_PREFERENCES` JSON
2. **Provider Initialization**: Load available providers based on API keys
3. **Fallback Handling**: Graceful degradation when providers fail
4. **Dynamic Loading**: Import providers only when needed

---

## Quick Start

### 1. Copy This Template

```bash
# Navigate to pets directory
cd /Users/andrewmaguire/LOCAL/Github/raggle-code/raggle-repo/core/pets/

# Copy template
cp -r _TEMPLATE_ my-workflow-manager
cd my-workflow-manager
```

### 2. Install Dependencies

```bash
npm install
```

### 3. Configure Environment

```bash
# Copy environment template
cp .env.example .env

# Edit with your API keys
nano .env
```

Example `.env`:
```bash
# Primary providers
GITHUB_TOKEN=ghp_your_github_token
GOOGLE_MAPS_API_KEY=your_google_maps_key
FAL_KEY=your_fal_api_key

# Provider preferences
PROVIDER_PREFERENCES={"maps":"googlemaps","ai":"fal"}

# Workflow settings
DEFAULT_TIMEOUT=30000
MAX_RETRIES=3
LOG_LEVEL=info
```

### 4. Test the Template

```bash
# Check provider status
opencode run "check provider status"

# Demo workflow
opencode run "demo workflow with geocode and enhance for 'New York'"
```

---

## Configuration Management

### Environment Variables

| Variable | Description | Required |
|----------|-------------|-----------|
| `GITHUB_TOKEN` | GitHub personal access token | Optional |
| `GOOGLE_MAPS_API_KEY` | Google Maps API key | Optional |
| `MAPBOX_ACCESS_TOKEN` | Mapbox access token | Optional |
| `FAL_KEY` | FAL AI API key | Optional |
| `PROVIDER_PREFERENCES` | JSON string with provider preferences | Optional |
| `DEFAULT_TIMEOUT` | Operation timeout in milliseconds | Optional |
| `MAX_RETRIES` | Maximum retry attempts | Optional |
| `LOG_LEVEL` | Logging level (debug/info/warn/error) | Optional |

### Provider Preferences

```json
{
  "maps": "googlemaps",
  "ai": "fal",
  "version_control": "github"
}
```

---

## Workflow Tools

### 1. workflow-demo

Demonstrates multi-provider workflow orchestration.

**Actions:**
- `geocode-and-enhance`: Geocode location and enhance with AI
- `create-and-tag`: Create repository with location-based tags
- `ai-process`: Process text using AI provider

**Examples:**
```bash
opencode run "demo workflow with geocode and enhance for 'San Francisco'"
opencode run "demo workflow with create and tag for 'my-project'"
opencode run "demo workflow with ai process for 'Hello world'"
```

### 2. manage-providers

Check provider status and availability.

**Actions:**
- `status`: Show all provider statuses
- `test`: Test provider connectivity
- `list`: List available and configured providers

**Examples:**
```bash
opencode run "check provider status"
opencode run "test all providers"
opencode run "list available providers"
```

### 3. manage-config

Manage plugin configuration.

**Actions:**
- `get`: Get configuration values
- `set`: Set configuration values
- `validate`: Validate configuration

**Examples:**
```bash
opencode run "get configuration"
opencode run "validate plugin configuration"
opencode run "set configuration key 'test.value' to 'demo'"
```

---

## Provider Integration

### Adding New Providers

1. **Create Provider Utils** (if not exists):
```bash
# Create new provider in src/utils/
mkdir src/utils/my-provider
cd src/utils/my-provider
```

2. **Implement Provider Interface**:
```typescript
// src/utils/my-provider/index.ts
export interface MyProviderConfig {
  apiKey: string
  apiUrl?: string
}

export class MyProviderAPI {
  constructor(private config: MyProviderConfig) {}
  
  async processData(data: string) {
    // Implementation
  }
}
```

3. **Update Template Configuration**:
```typescript
interface PluginConfig {
  providers: {
    // Add new provider
    myprovider?: { apiKey: string; apiUrl?: string }
  }
}
```

4. **Add Provider Initialization**:
```typescript
const initializeProviders = async (config: PluginConfig) => {
  const providers: any = {}
  
  if (config.providers.myprovider) {
    const { MyProviderAPI } = await import('../../src/utils/my-provider')
    providers.myprovider = new MyProviderAPI(config.providers.myprovider)
  }
  
  return providers
}
```

---

## Best Practices

### 1. Factory Pattern Usage

```typescript
// ✅ Good: Use factory for tool creation
const tools = [
  {
    name: "my-tool",
    description: "Tool description",
    schema: z.object({ /* ... */ }),
    execute: async (args) => { /* ... */ }
  }
]

return createPlugin(tools)

// ❌ Bad: Direct tool definition
return {
  tool: {
    "my-tool": tool({ /* ... */ })
  }
}
```

### 2. Provider Management

```typescript
// ✅ Good: Lazy loading with error handling
const initializeProvider = async (config: any) => {
  try {
    const ProviderAPI = await import('../../src/utils/provider')
    return new ProviderAPI(config)
  } catch (error) {
    console.warn(`Provider initialization failed:`, error)
    return null
  }
}

// ❌ Bad: Eager loading without error handling
const provider = new ProviderAPI(config)  // Fails if import errors
```

### 3. Configuration Validation

```typescript
// ✅ Good: Comprehensive validation
const validateConfig = (config: PluginConfig) => {
  const errors: string[] = []
  
  if (!config.providers.github && !config.providers.maps) {
    errors.push("At least one provider must be configured")
  }
  
  return { valid: errors.length === 0, errors }
}

// ❌ Bad: No validation
const providers = await initializeProviders(config)  // May fail silently
```

### 4. Error Handling

```typescript
// ✅ Good: Structured error responses
return createResponse(false, undefined, [{
  error: error.message,
  provider: providerName,
  action: args.action
}], { duration: Date.now() - startTime })

// ❌ Bad: Unstructured errors
throw new Error(`Operation failed: ${error.message}`)
```

---

## Migration Guide

### From Legacy Plugin Structure

**Old Structure:**
```typescript
export const LegacyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    tool: {
      "tool-name": tool({
        description: "...",
        args: { /* ... */ },
        execute: async (args) => { /* ... */ }
      })
    }
  }
}
```

**New Factory Structure:**
```typescript
export const FactoryPlugin = async () => {
  const config = loadConfiguration()
  const providers = await initializeProviders(config)
  const tools = createWorkflowTools(providers, config)
  
  return createPlugin(tools)
}
```

### Migration Steps

1. **Remove Legacy Parameters**: Plugin no longer receives `{ project, client, $, directory, worktree }`
2. **Add Configuration Loading**: Implement `loadConfiguration()` function
3. **Add Provider Management**: Implement `initializeProviders()` function
4. **Convert Tools to Factory**: Use `createPlugin()` instead of direct tool objects
5. **Update Tool Schemas**: Use Zod schemas instead of JSON schemas
6. **Add Error Handling**: Implement structured error responses

---

## Testing

### Unit Testing

```bash
# Test individual tools
npm run test:queries

# Test workflow scenarios
npm run test:scenarios

# Test all functionality
npm run test:all
```

### Provider Testing

```bash
# Test provider connectivity
opencode run "test all providers"

# Test provider fallbacks
# Temporarily disable primary provider and test fallbacks
```

### Configuration Testing

```bash
# Test with missing environment variables
unset GITHUB_TOKEN
opencode run "validate plugin configuration"

# Test with invalid configuration
PROVIDER_PREFERENCES='{"invalid":"provider"}' opencode run "check provider status"
```

---

## Advanced Patterns

### 1. Dynamic Provider Discovery

```typescript
const discoverProviders = async () => {
  const providerDir = '../../src/utils'
  const providers = await fs.readdir(providerDir)
  
  return Promise.all(
    providers
      .filter(name => !name.startsWith('.'))
      .map(async name => {
        try {
          const module = await import(`${providerDir}/${name}`)
          return { name, module: module.default || module }
        } catch {
          return { name, error: 'Failed to load' }
        }
      })
  )
}
```

### 2. Provider Health Monitoring

```typescript
const monitorProviderHealth = async (providers: any) => {
  const health = await Promise.allSettled(
    Object.entries(providers).map(async ([name, provider]) => [
      name,
      {
        status: await testProvider(provider),
        lastCheck: new Date().toISOString(),
        responseTime: await measureResponseTime(provider)
      }
    ])
  )
  
  return Object.fromEntries(health)
}
```

### 3. Workflow Composition

```typescript
const composeWorkflow = (steps: WorkflowStep[]) => async (input: any) => {
  let result = input
  
  for (const step of steps) {
    try {
      result = await step.execute(result)
    } catch (error) {
      if (step.required) {
        throw error
      }
      console.warn(`Optional step ${step.name} failed:`, error)
    }
  }
  
  return result
}
```

---

## Directory Structure

```
my-workflow-manager/
├── index.ts              # Factory-based plugin implementation
├── package.json          # Dependencies and metadata
├── README.md             # This documentation
├── .env.example          # Environment variable template
├── .env                  # Your actual configuration (gitignored)
├── .gitignore           # Git ignore file
└── opencode.json        # OpenCode configuration
```

---

## Need Help?

- **Factory Pattern**: See `src/core/plugin-factory.ts`
- **Provider Examples**: See `src/utils/` directory
- **Documentation**: See `prompts/pet-creator.md` for LLM guidance

---

## License

MIT - feel free to use this template for your own workflow managers!

---

**Happy workflow building! 🚀**