# Complete Reference

Comprehensive reference for @majkapp/plugin-kit covering all features and patterns.

## Table of Contents

1. [Quick Start](#quick-start)
2. [Plugin Definition](#plugin-definition)
3. [Functions](#functions)
4. [Screens](#screens)
5. [Generated Hooks](#generated-hooks)
6. [Context API](#context-api)
7. [AI API](#ai-api)
8. [Services](#services)
9. [Teammates](#teammates)
10. [Lifecycle](#lifecycle)
11. [Testing](#testing)
12. [Configuration](#configuration)
13. [Best Practices](#best-practices)

## Quick Start

```typescript
// src/index.ts
import { definePlugin } from '@majkapp/plugin-kit';

const plugin = definePlugin('my-plugin', 'My Plugin', '1.0.0')
  .pluginRoot(__dirname)
  .function('health', {
    description: 'Check plugin health',
    input: { type: 'object', properties: {}, additionalProperties: false },
    output: {
      type: 'object',
      properties: {
        status: { type: 'string' },
        timestamp: { type: 'string' }
      }
    },
    handler: async (_, ctx) => ({
      status: 'ok',
      timestamp: new Date().toISOString()
    })
  })
  .screenReact({
    id: 'my-plugin-main',
    name: 'My Plugin',
    description: 'Main screen',
    route: '/plugin-screens/my-plugin/main',
    pluginPath: '/index.html'
  })
  .build();

export = plugin;
```

## Plugin Definition

### definePlugin()

```typescript
definePlugin(id: string, name: string, version: string)
```

- **id**: Unique identifier (lowercase, hyphens)
- **name**: Display name
- **version**: Semantic version

### .pluginRoot()

**REQUIRED.** Enables resource loading.

```typescript
.pluginRoot(__dirname)
```

### .build()

**REQUIRED.** Finalizes plugin definition.

```typescript
.build()
```

Always at the end.

## Functions

Define callable operations with typed inputs/outputs.

### Basic Function

```typescript
.function('functionName', {
  description: string,          // REQUIRED: 2-3 sentences
  input: JsonSchema,            // REQUIRED: Input schema
  output: JsonSchema,           // REQUIRED: Output schema
  handler: HandlerFn,           // REQUIRED: Implementation
  tags?: string[],              // Optional: Organization tags
  deprecated?: boolean          // Optional: Mark deprecated
})
```

### Input/Output Schemas

JSON Schema for validation:

```typescript
// Simple types
{ type: 'string' }
{ type: 'number' }
{ type: 'boolean' }

// Enums
{ type: 'string', enum: ['value1', 'value2'] }

// Objects
{
  type: 'object',
  properties: {
    name: { type: 'string' },
    age: { type: 'number' }
  },
  required: ['name'],
  additionalProperties: false
}

// Arrays
{
  type: 'array',
  items: { type: 'string' }
}
```

### Handler Function

```typescript
handler: async (input, ctx) => {
  // ctx.logger - Logging
  // ctx.storage - Key-value store
  // ctx.majk - MAJK APIs

  return output; // Must match output schema
}
```

### Example: Analytics Function

```typescript
.function('getAnalytics', {
  description: 'Get analytics for specified time period',
  input: {
    type: 'object',
    properties: {
      period: { type: 'string', enum: ['24h', '7d', '30d'] }
    },
    required: ['period']
  },
  output: {
    type: 'object',
    properties: {
      period: { type: 'string' },
      metrics: {
        type: 'object',
        properties: {
          activeUsers: { type: 'number' },
          totalSessions: { type: 'number' }
        }
      }
    }
  },
  handler: async (input, ctx) => {
    const sessions = await ctx.storage.get('sessions') || [];
    // Calculate metrics...
    return { period: input.period, metrics: { /* ... */ } };
  }
})
```

## Screens

Define UI screens loaded in MAJK.

### React Screen

```typescript
.screenReact({
  id: string,                           // Unique ID
  name: string,                         // Display name
  description: string,                  // 2-3 sentences
  route: `/plugin-screens/${id}/path`, // Must start with prefix
  pluginPath: string,                   // Path to built app
  pluginPathHash?: string               // Optional hash route
})
```

### Example: Multiple Screens

```typescript
.screenReact({
  id: 'my-plugin-dashboard',
  name: 'Dashboard',
  description: 'Main dashboard',
  route: '/plugin-screens/my-plugin/dashboard',
  pluginPath: '/index.html',
  pluginPathHash: '#/'
})

.screenReact({
  id: 'my-plugin-settings',
  name: 'Settings',
  description: 'Plugin settings',
  route: '/plugin-screens/my-plugin/settings',
  pluginPath: '/index.html',
  pluginPathHash: '#/settings'
})
```

### React App Setup

```typescript
// ui/src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';

export function App() {
  return (
    <BrowserRouter basename={window.__MAJK_IFRAME_BASE__}>
      <Routes>
        <Route path="/" element={<DashboardPage />} />
        <Route path="/settings" element={<SettingsPage />} />
      </Routes>
    </BrowserRouter>
  );
}
```

## Generated Hooks

Auto-generated React hooks from functions.

### Query Hooks

Auto-fetch on mount:

```typescript
const { data, error, loading, refetch } = useHealth(
  input?,
  options?: {
    enabled?: boolean,
    refetchInterval?: number
  }
);
```

### Mutation Hooks

Call manually:

```typescript
const { mutate, data, error, loading } = useClearEvents();

await mutate(input);
```

### Example: Using Hooks

```typescript
import { useHealth, useGetAnalytics, useClearEvents } from './generated/hooks';

export function DashboardPage() {
  // Query - auto-fetches
  const { data: health } = useHealth();
  const { data: analytics } = useGetAnalytics({ period: '7d' });

  // Mutation - call manually
  const { mutate: clearEvents } = useClearEvents();

  return (
    <div>
      <div data-majk-id="healthStatus">{health?.status}</div>
      <button onClick={() => clearEvents({})}>Clear</button>
    </div>
  );
}
```

## Context API

Handler context provides access to MAJK.

### ctx.logger

```typescript
ctx.logger.debug(message, data);
ctx.logger.info(message, data);
ctx.logger.warn(message, data);
ctx.logger.error(message, data);
```

### ctx.storage

Plugin-scoped key-value store:

```typescript
await ctx.storage.get(key);
await ctx.storage.set(key, value);
await ctx.storage.has(key);
await ctx.storage.delete(key);
await ctx.storage.clear();
```

### ctx.majk APIs

```typescript
// Conversations
await ctx.majk.conversations.list();
await ctx.majk.conversations.get(id);

// Todos
await ctx.majk.todos.list();
await ctx.majk.todos.get(id);

// Projects
await ctx.majk.projects.list();
await ctx.majk.projects.get(id);

// Teammates
await ctx.majk.teammates.list();
await ctx.majk.teammates.get(id);

// MCP Servers
await ctx.majk.mcpServers.list();
await ctx.majk.mcpServers.get(id);

// Event Bus (use in .onReady())
const subscription = await ctx.majk.eventBus.subscribeAll(handler);
```

## AI API

Access AI providers and language models with a unified, provider-agnostic interface.

### Basic Usage

```typescript
// Get default LLM
const llm = ctx.majk.ai.getDefaultLLM();

// Send a prompt
const result = await llm.prompt({
  messages: [
    { role: 'system', content: 'You are a helpful assistant' },
    { role: 'user', content: 'What is TypeScript?' }
  ],
  temperature: 0.7,
  maxTokens: 500
});

console.log(result.content);
console.log(`Tokens used: ${result.usage?.totalTokens}`);
```

### Provider Management

```typescript
// List all providers
const providers = ctx.majk.ai.listProviders();
for (const provider of providers) {
  console.log(`${provider.name}: ${provider.capabilities}`);
}

// Get specific provider
const bedrock = ctx.majk.ai.getProvider('bedrock');
if (bedrock) {
  const claude = bedrock.getLLM('anthropic.claude-3-5-sonnet-20241022-v2:0');
}

// Query by capability
const imageProviders = ctx.majk.ai.getProvidersWithCapability('imageGeneration');

// Check status
const status = await ctx.majk.ai.getProviderStatus('bedrock');
console.log(`Available: ${status.available}, Authenticated: ${status.authenticated}`);
```

### Streaming Responses

```typescript
const stream = llm.promptStream({
  messages: [{ role: 'user', content: 'Tell me a story' }]
});

for await (const chunk of stream) {
  if (chunk.type === 'content_delta') {
    process.stdout.write(chunk.content);
  }
}
```

### Structured JSON Output

```typescript
const result = await llm.promptForJson({
  messages: [
    { role: 'user', content: 'Analyze sentiment: "This is amazing!"' }
  ],
  schema: {
    type: 'object',
    properties: {
      sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
      confidence: { type: 'number' }
    },
    required: ['sentiment', 'confidence']
  }
});

console.log(result.sentiment); // Type-safe access
```

### Advanced Features

```typescript
// Function calling
const result = await llm.functionCall({
  messages: [{ role: 'user', content: 'What is the weather?' }],
  functions: [{
    name: 'getWeather',
    description: 'Get weather for a location',
    parameters: {
      type: 'object',
      properties: {
        location: { type: 'string' }
      }
    }
  }]
});

// Image generation (if provider supports it)
const imageProviders = ctx.majk.ai.getProvidersWithCapability('imageGeneration');
if (imageProviders.length > 0) {
  const image = await imageProviders[0].generateImage?.({
    prompt: 'A beautiful sunset',
    size: '1024x1024'
  });
}

// Text embeddings (if provider supports it)
const embedding = await provider.generateEmbedding?.('Some text to embed');

// Audio transcription (if provider supports it)
const transcription = await provider.transcribeAudio?.({
  audio: audioBuffer,
  format: 'mp3'
});
```

### Error Handling

```typescript
import { AIProviderError } from '@majkapp/plugin-kit';

try {
  const result = await llm.prompt({ messages });
} catch (error) {
  if (error instanceof AIProviderError) {
    console.error(`AI Error [${error.code}]: ${error.message}`);

    if (error.code === 'RATE_LIMIT_EXCEEDED') {
      // Handle rate limiting
    }
    if (error.code === 'AUTHENTICATION_FAILED') {
      // Handle auth errors
    }
  }
}
```

For complete AI documentation, run: `npx @majkapp/plugin-kit --ai`

## Services

Group related functions together.

### Service Definition

```typescript
.service(serviceName, {
  type: string,
  metadata: {
    name: string,
    description: string,
    version: string
  },
  discoverable?: boolean
})
  .withFunction(functionName, {
    examples?: string[],
    tags?: string[]
  })
.endService()
```

### Example

```typescript
.function('health', { /* ... */, tags: ['monitoring'] })
.function('getStats', { /* ... */, tags: ['monitoring'] })

.service('plugin:my-plugin:monitoring', {
  type: '@my-plugin/monitoring',
  metadata: {
    name: 'Monitoring Service',
    description: 'Health checks and statistics',
    version: '1.0.0'
  }
})
  .withFunction('health', {
    examples: ['Check plugin health']
  })
  .withFunction('getStats', {
    examples: ['Get statistics']
  })
.endService()
```

## Teammates

Define AI agents that users can interact with.

### Auto-Generated Default Teammate

Every plugin automatically gets a default teammate with access to ALL functions.

**Default Behavior**:
- Name: `"<Plugin Name> Expert"`
- ID: Same as package name (e.g., `"@majk/my-plugin"`)
- Functions: ALL plugin functions
- System Prompt: Generic (customizable)

### Custom Instructions

```typescript
.instructions(`You are an expert assistant for My Plugin.

Your capabilities:
- Function 1 using func1
- Function 2 using func2

When helping users:
1. Explain what you're doing
2. Present results clearly
3. Suggest next steps`)
```

### Disable Default Teammate

```typescript
.noDefaultTeammate()
```

### Custom Teammates

```typescript
.teamMember([
  {
    id: 'viewer',
    name: 'Viewer (Read-Only)',
    systemPrompt: 'You are a read-only assistant...',
    expertise: ['Data Viewing'],
    skills: {
      primary: ['Query'],
      secondary: [],
      languages: [],
      frameworks: [],
      tools: []
    },
    personality: {
      workStyle: 'methodical',
      codeStyle: 'concise',
      reviewStyle: 'pragmatic',
      communicationStyle: 'explanatory'
    },
    metadata: {
      totalTasks: 0,
      successfulTasks: 0,
      projectHistory: [],
      codeStats: {
        linesWritten: 0,
        filesModified: 0,
        testsWritten: 0,
        reviewsPassed: 0,
        commitsCreated: 0
      }
    },
    mcpServerIds: [],
    functions: [
      'getData',       // Simple: current plugin
      'searchData',
      {                // Advanced: cross-plugin
        pluginId: '@other/plugin',
        serviceName: '@other/plugin:all',
        functionName: 'otherFunc'
      }
    ]
  }
])
```

See `npx @majkapp/plugin-kit --teammates` for complete guide.

## Lifecycle

Initialize and clean up resources.

### .onReady()

```typescript
.onReady(async (ctx, cleanup) => {
  // Initialization
  const subscription = await ctx.majk.eventBus.subscribeAll(handler);

  // Register cleanup
  cleanup(() => {
    subscription.unsubscribe();
  });
})
```

### Example: Event Subscription

```typescript
const eventLog = [];

.function('getEvents', {
  /* ... */
  handler: async () => ({ events: eventLog })
})

.onReady(async (ctx, cleanup) => {
  const subscription = await ctx.majk.eventBus.subscribeAll((event) => {
    eventLog.unshift({
      id: `${Date.now()}`,
      type: event.type,
      entityType: event.entityType,
      timestamp: new Date().toISOString()
    });

    if (eventLog.length > 1000) {
      eventLog.length = 1000;
    }
  });

  cleanup(() => {
    subscription.unsubscribe();
  });
})
```

## Testing

Test functions and UI with @majkapp/plugin-test.

### Function Tests

```typescript
import { test, invoke, mock } from '@majkapp/plugin-test';
import assert from 'assert';

test('function test', async () => {
  const context = mock()
    .storage({ key: 'value' })
    .withMajkData({
      conversations: [{ id: 'c1', title: 'Test' }]
    })
    .build();

  const result = await invoke('functionName', { input }, { context });

  assert.strictEqual(result.expected, 'value');
});
```

### UI Tests

```typescript
import { test, bot, mock } from '@majkapp/plugin-test';
import assert from 'assert';

test('ui test', async () => {
  const context = mock()
    .withMockHandler('health', async () => ({
      status: 'ok',
      timestamp: new Date().toISOString()
    }))
    .build();

  const b = await bot(context);
  await b.goto('/plugin-screens/my-plugin/main');

  await b.waitForSelector('[data-majk-id="healthStatus"][data-majk-state="populated"]');

  const text = await b.getText('[data-majk-id="healthStatus"]');
  assert.ok(text.includes('ok'));

  await b.close();
});
```

### data-majk-* Attributes

```typescript
// Identify elements
<button data-majk-id="saveButton">Save</button>

// Track state
<div data-majk-id="container" data-majk-state="populated">
  {/* content */}
</div>
```

## Configuration

### vite.config.js (EXACT FORMAT)

```javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  root: 'ui',
  base: '',  // IMPORTANT: Empty string
  server: {
    port: 3000,
    strictPort: false,
  },
  build: {
    outDir: '../dist',
    assetsDir: 'assets',
    emptyOutDir: true,
  },
});
```

### Project Structure

```
my-plugin/
├── src/
│   ├── index.ts
│   └── core/
├── ui/
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   └── generated/
│   └── vite.config.js
├── tests/
│   └── plugin/
│       ├── functions/unit/
│       └── ui/unit/
├── dist/
├── package.json
└── tsconfig.json
```

## Best Practices

### 1. Decouple Business Logic

```typescript
// src/core/analytics.ts - Pure logic
export function calculateAnalytics(sessions, period) {
  // Pure computation, no plugin dependencies
}

// src/index.ts - Plugin wrapper
.function('getAnalytics', {
  /* ... */
  handler: async (input, ctx) => {
    const sessions = await ctx.storage.get('sessions') || [];
    return calculateAnalytics(sessions, input.period);
  }
})
```

### 2. Always Use data-majk-* Attributes

```typescript
<button
  data-majk-id="saveButton"
  data-majk-state={saving ? 'saving' : 'idle'}
  onClick={handleSave}
>
  Save
</button>
```

### 3. Create uiLog Function

```typescript
.function('uiLog', {
  description: 'Log from UI',
  input: {
    type: 'object',
    properties: {
      level: { type: 'string', enum: ['debug', 'info', 'warn', 'error'] },
      component: { type: 'string' },
      message: { type: 'string' },
      data: { type: 'object' }
    },
    required: ['level', 'component', 'message']
  },
  output: {
    type: 'object',
    properties: { success: { type: 'boolean' } }
  },
  handler: async (input, ctx) => {
    ctx.logger[input.level](`[UI:${input.component}] ${input.message}`, input.data);
    return { success: true };
  }
})
```

### 4. Register Cleanup in onReady

```typescript
.onReady(async (ctx, cleanup) => {
  const subscription = await ctx.majk.eventBus.subscribeAll(handler);

  // ALWAYS register cleanup
  cleanup(() => {
    subscription.unsubscribe();
  });
})
```

### 5. Service Interfaces for External Dependencies

```typescript
// src/core/storage-service.ts
export interface StorageService {
  save(key: string, data: any): Promise<void>;
  load(key: string): Promise<any>;
}

// src/core/aws-storage.ts - Real implementation
export class AWSStorageService implements StorageService {
  // AWS SDK implementation
}

// src/core/mock-storage.ts - Mock for testing
export class MockStorageService implements StorageService {
  // In-memory implementation for tests
}
```

## Complete Example

```typescript
// src/index.ts
import { definePlugin } from '@majkapp/plugin-kit';

const eventLog: any[] = [];
const maxEvents = 500;

const plugin = definePlugin('my-plugin', 'My Plugin', '1.0.0')
  .pluginRoot(__dirname)

  // Health function
  .function('health', {
    description: 'Check plugin health',
    input: {
      type: 'object',
      properties: {},
      additionalProperties: false
    },
    output: {
      type: 'object',
      properties: {
        status: { type: 'string' },
        timestamp: { type: 'string' }
      }
    },
    handler: async (_, ctx) => ({
      status: 'ok',
      timestamp: new Date().toISOString()
    }),
    tags: ['monitoring']
  })

  // Analytics function
  .function('getAnalytics', {
    description: 'Get analytics',
    input: {
      type: 'object',
      properties: {
        period: { type: 'string', enum: ['24h', '7d', '30d'] }
      },
      required: ['period']
    },
    output: {
      type: 'object',
      properties: {
        period: { type: 'string' },
        metrics: {
          type: 'object',
          properties: {
            activeUsers: { type: 'number' },
            totalSessions: { type: 'number' }
          }
        }
      }
    },
    handler: async (input, ctx) => {
      const sessions = await ctx.storage.get('sessions') || [];
      // Calculate...
      return { period: input.period, metrics: { /* ... */ } };
    },
    tags: ['analytics']
  })

  // Events functions
  .function('getEvents', {
    description: 'Get logged events',
    input: {
      type: 'object',
      properties: {},
      additionalProperties: false
    },
    output: {
      type: 'object',
      properties: {
        events: { type: 'array' },
        count: { type: 'number' }
      }
    },
    handler: async () => ({
      events: eventLog,
      count: eventLog.length
    }),
    tags: ['events']
  })

  .function('clearEvents', {
    description: 'Clear event log',
    input: {
      type: 'object',
      properties: {},
      additionalProperties: false
    },
    output: {
      type: 'object',
      properties: {
        success: { type: 'boolean' },
        clearedCount: { type: 'number' }
      }
    },
    handler: async (_, ctx) => {
      const count = eventLog.length;
      eventLog.length = 0;
      ctx.logger.info(`Cleared ${count} events`);
      return { success: true, clearedCount: count };
    },
    tags: ['events']
  })

  // UI logging
  .function('uiLog', {
    description: 'Log from UI',
    input: {
      type: 'object',
      properties: {
        level: { type: 'string', enum: ['debug', 'info', 'warn', 'error'] },
        component: { type: 'string' },
        message: { type: 'string' },
        data: { type: 'object' }
      },
      required: ['level', 'component', 'message']
    },
    output: {
      type: 'object',
      properties: { success: { type: 'boolean' } }
    },
    handler: async (input, ctx) => {
      ctx.logger[input.level](`[UI:${input.component}] ${input.message}`, input.data);
      return { success: true };
    },
    tags: ['debugging']
  })

  // Screens
  .screenReact({
    id: 'my-plugin-dashboard',
    name: 'Dashboard',
    description: 'Main dashboard',
    route: '/plugin-screens/my-plugin/dashboard',
    pluginPath: '/index.html',
    pluginPathHash: '#/'
  })

  .screenReact({
    id: 'my-plugin-events',
    name: 'Events',
    description: 'Event monitor',
    route: '/plugin-screens/my-plugin/events',
    pluginPath: '/index.html',
    pluginPathHash: '#/events'
  })

  // Services
  .service('plugin:my-plugin:monitoring', {
    type: '@my-plugin/monitoring',
    metadata: {
      name: 'Monitoring Service',
      description: 'Health checks and analytics',
      version: '1.0.0'
    }
  })
    .withFunction('health')
    .withFunction('getAnalytics')
  .endService()

  .service('plugin:my-plugin:events', {
    type: '@my-plugin/events',
    metadata: {
      name: 'Event Management',
      description: 'Event logging and monitoring',
      version: '1.0.0'
    }
  })
    .withFunction('getEvents')
    .withFunction('clearEvents')
  .endService()

  // Lifecycle
  .onReady(async (ctx, cleanup) => {
    ctx.logger.info('My Plugin initializing');

    // Subscribe to events
    const subscription = await ctx.majk.eventBus.subscribeAll((event) => {
      eventLog.unshift({
        id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        timestamp: new Date().toISOString(),
        entityType: event.entityType,
        eventType: event.type
      });

      if (eventLog.length > maxEvents) {
        eventLog.length = maxEvents;
      }
    });

    cleanup(() => {
      subscription.unsubscribe();
      ctx.logger.info('Event subscription cleaned up');
    });

    ctx.logger.info('My Plugin ready');
  })

  .build();

export = plugin;
```

## Next Steps

Run `npx @majkapp/plugin-kit` - Quick start
Run `npx @majkapp/plugin-kit --functions` - Function patterns
Run `npx @majkapp/plugin-kit --screens` - Screen configuration
Run `npx @majkapp/plugin-kit --hooks` - Generated hooks
Run `npx @majkapp/plugin-kit --context` - Context API
Run `npx @majkapp/plugin-kit --services` - Service grouping
Run `npx @majkapp/plugin-kit --lifecycle` - Lifecycle hooks
Run `npx @majkapp/plugin-kit --testing` - Testing guide
Run `npx @majkapp/plugin-kit --config` - Project configuration
