directus-task-management/planning/06-implementation-guides.md

39 KiB

Directus Task Management Suite - Implementation Guides

Implementation Overview

This document provides detailed technical implementation guides for building the Directus Task Management Suite, organized by development phase with specific code examples, configuration steps, and best practices.

Phase 1: Foundation Implementation Guide

1.1 Directus Collections Setup

Core Collections Schema Implementation

Step 1: Create Projects Collection

-- Execute via Directus API or admin interface
{
  "collection": "projects",
  "schema": {
    "id": {
      "type": "uuid",
      "default": "$GENERATE_UUID",
      "primary_key": true
    },
    "name": {
      "type": "string",
      "required": true,
      "max_length": 255
    },
    "description": {
      "type": "text",
      "nullable": true
    },
    "status": {
      "type": "string",
      "default": "active",
      "options": {
        "choices": [
          {"text": "Active", "value": "active"},
          {"text": "On Hold", "value": "on_hold"},
          {"text": "Completed", "value": "completed"},
          {"text": "Archived", "value": "archived"}
        ]
      }
    },
    "parent_project": {
      "type": "uuid",
      "foreign_key": "projects.id",
      "nullable": true
    },
    "repository_url": {
      "type": "string",
      "nullable": true
    },
    "bmad_workflow_type": {
      "type": "string",
      "options": {
        "choices": [
          {"text": "Greenfield", "value": "greenfield"},
          {"text": "Brownfield", "value": "brownfield"},
          {"text": "Maintenance", "value": "maintenance"}
        ]
      }
    },
    "task_master_project_id": {
      "type": "string",
      "nullable": true
    },
    "priority": {
      "type": "string",
      "default": "medium",
      "options": {
        "choices": [
          {"text": "Low", "value": "low"},
          {"text": "Medium", "value": "medium"},
          {"text": "High", "value": "high"},
          {"text": "Critical", "value": "critical"}
        ]
      }
    },
    "start_date": {
      "type": "date",
      "nullable": true
    },
    "due_date": {
      "type": "date",
      "nullable": true
    },
    "completion_percentage": {
      "type": "integer",
      "default": 0,
      "validation": {
        "min": 0,
        "max": 100
      }
    },
    "created_by": {
      "type": "uuid",
      "foreign_key": "directus_users.id"
    },
    "assigned_to": {
      "type": "uuid",
      "foreign_key": "directus_users.id",
      "nullable": true
    },
    "metadata": {
      "type": "json",
      "nullable": true
    },
    "created_at": {
      "type": "timestamp",
      "auto_create": true
    },
    "updated_at": {
      "type": "timestamp", 
      "auto_update": true
    }
  }
}

Step 2: Create Task Statuses Collection

{
  "collection": "task_statuses",
  "schema": {
    "id": {
      "type": "uuid",
      "default": "$GENERATE_UUID",
      "primary_key": true
    },
    "name": {
      "type": "string",
      "required": true,
      "unique": true
    },
    "slug": {
      "type": "string",
      "required": true,
      "unique": true
    },
    "description": {
      "type": "text",
      "nullable": true
    },
    "color": {
      "type": "string",
      "default": "#6366f1",
      "validation": {
        "pattern": "^#[0-9A-Fa-f]{6}$"
      }
    },
    "icon": {
      "type": "string",
      "default": "circle"
    },
    "category": {
      "type": "string",
      "required": true,
      "options": {
        "choices": [
          {"text": "To Do", "value": "todo"},
          {"text": "In Progress", "value": "in_progress"},
          {"text": "Review", "value": "review"},
          {"text": "Done", "value": "done"},
          {"text": "Blocked", "value": "blocked"}
        ]
      }
    },
    "is_final": {
      "type": "boolean",
      "default": false
    },
    "auto_transition_rules": {
      "type": "json",
      "nullable": true
    },
    "workflow_type": {
      "type": "string",
      "options": {
        "choices": [
          {"text": "BMad", "value": "bmad"},
          {"text": "Task Master", "value": "task_master"},
          {"text": "GitHub", "value": "github"},
          {"text": "Custom", "value": "custom"}
        ]
      }
    },
    "sort_order": {
      "type": "integer",
      "default": 0
    },
    "active": {
      "type": "boolean",
      "default": true
    },
    "created_at": {
      "type": "timestamp",
      "auto_create": true
    },
    "updated_at": {
      "type": "timestamp",
      "auto_update": true
    }
  }
}

Step 3: Create Tasks Collection

{
  "collection": "tasks",
  "schema": {
    "id": {
      "type": "uuid",
      "default": "$GENERATE_UUID",
      "primary_key": true
    },
    "title": {
      "type": "string",
      "required": true,
      "max_length": 255
    },
    "description": {
      "type": "text",
      "nullable": true
    },
    "status": {
      "type": "uuid",
      "foreign_key": "task_statuses.id",
      "required": true
    },
    "priority": {
      "type": "string",
      "default": "medium",
      "options": {
        "choices": [
          {"text": "Lowest", "value": "lowest"},
          {"text": "Low", "value": "low"},
          {"text": "Medium", "value": "medium"},
          {"text": "High", "value": "high"},
          {"text": "Highest", "value": "highest"}
        ]
      }
    },
    "task_type": {
      "type": "string",
      "default": "feature",
      "options": {
        "choices": [
          {"text": "Feature", "value": "feature"},
          {"text": "Bug", "value": "bug"},
          {"text": "Enhancement", "value": "enhancement"},
          {"text": "Research", "value": "research"},
          {"text": "Maintenance", "value": "maintenance"}
        ]
      }
    },
    "complexity": {
      "type": "string",
      "options": {
        "choices": [
          {"text": "Trivial", "value": "trivial"},
          {"text": "Minor", "value": "minor"},
          {"text": "Major", "value": "major"},
          {"text": "Critical", "value": "critical"}
        ]
      }
    },
    "story_points": {
      "type": "integer",
      "nullable": true,
      "validation": {
        "min": 1,
        "max": 21
      }
    },
    "project": {
      "type": "uuid",
      "foreign_key": "projects.id",
      "required": true
    },
    "parent_task": {
      "type": "uuid",
      "foreign_key": "tasks.id",
      "nullable": true
    },
    "epic": {
      "type": "uuid",
      "foreign_key": "tasks.id",
      "nullable": true
    },
    "created_by": {
      "type": "uuid",
      "foreign_key": "directus_users.id"
    },
    "assigned_to": {
      "type": "uuid",
      "foreign_key": "directus_users.id",
      "nullable": true
    },
    "ai_agent_assigned": {
      "type": "string",
      "nullable": true
    },
    "reviewer": {
      "type": "uuid",
      "foreign_key": "directus_users.id",
      "nullable": true
    },
    "estimated_hours": {
      "type": "decimal",
      "precision": 5,
      "scale": 2,
      "nullable": true
    },
    "actual_hours": {
      "type": "decimal",
      "precision": 5,
      "scale": 2,
      "computed": "SUM(task_time_entries.hours)"
    },
    "progress_percentage": {
      "type": "integer",
      "default": 0,
      "validation": {
        "min": 0,
        "max": 100
      }
    },
    "start_date": {
      "type": "date",
      "nullable": true
    },
    "due_date": {
      "type": "date",
      "nullable": true
    },
    "completed_at": {
      "type": "timestamp",
      "nullable": true
    },
    "task_master_id": {
      "type": "string",
      "nullable": true,
      "unique": true
    },
    "github_issue_url": {
      "type": "string",
      "nullable": true
    },
    "bmad_story_id": {
      "type": "string",
      "nullable": true
    },
    "external_refs": {
      "type": "json",
      "nullable": true
    },
    "ai_generated": {
      "type": "boolean",
      "default": false
    },
    "ai_context": {
      "type": "json",
      "nullable": true
    },
    "auto_status_updates": {
      "type": "boolean",
      "default": false
    },
    "acceptance_criteria": {
      "type": "text",
      "nullable": true
    },
    "definition_of_done": {
      "type": "text",
      "nullable": true
    },
    "notes": {
      "type": "text",
      "nullable": true
    },
    "created_at": {
      "type": "timestamp",
      "auto_create": true
    },
    "updated_at": {
      "type": "timestamp",
      "auto_update": true
    }
  }
}

Database Optimization Setup

Step 4: Create Strategic Indexes

-- Composite indexes for common query patterns
CREATE INDEX idx_tasks_project_status ON tasks(project, status);
CREATE INDEX idx_tasks_assigned_status ON tasks(assigned_to, status) WHERE assigned_to IS NOT NULL;
CREATE INDEX idx_tasks_due_date ON tasks(due_date) WHERE due_date IS NOT NULL;

-- Partial indexes for active data
CREATE INDEX idx_active_tasks ON tasks(project, status) 
WHERE status NOT IN (
  SELECT id FROM task_statuses WHERE is_final = true
);

-- Full-text search optimization
CREATE INDEX idx_tasks_search ON tasks 
USING gin(to_tsvector('english', title || ' ' || COALESCE(description, '')));

-- Project hierarchy optimization
CREATE INDEX idx_projects_parent ON projects(parent_project) WHERE parent_project IS NOT NULL;
CREATE INDEX idx_tasks_parent ON tasks(parent_task) WHERE parent_task IS NOT NULL;

1.2 MCP Tools Implementation

MCP Server Extension Setup

Step 1: Project Structure Setup

# Create MCP extension directory
mkdir -p tools/directus-task-management-mcp
cd tools/directus-task-management-mcp

# Initialize package.json
npm init -y
npm install @modelcontextprotocol/sdk-typescript
npm install @types/node typescript --save-dev

# Create source structure
mkdir -p src/{tools,types,utils}

Step 2: Core MCP Tools Implementation

src/tools/task-crud.ts

import { Tool } from "@modelcontextprotocol/sdk-typescript";
import { DirectusClient } from "../utils/directus-client";
import { Task, TaskCreateData, TaskUpdateData, TaskFilters } from "../types/task-types";

export const createTaskTool: Tool = {
  name: "mcp__directus__create_task",
  description: "Create a new task in Directus with full metadata support",
  inputSchema: {
    type: "object",
    properties: {
      data: {
        type: "object",
        properties: {
          title: { type: "string" },
          description: { type: "string" },
          project: { type: "string", format: "uuid" },
          priority: { type: "string", enum: ["lowest", "low", "medium", "high", "highest"] },
          task_type: { type: "string", enum: ["feature", "bug", "enhancement", "research", "maintenance"] },
          complexity: { type: "string", enum: ["trivial", "minor", "major", "critical"] },
          assigned_to: { type: "string", format: "uuid" },
          estimated_hours: { type: "number" },
          due_date: { type: "string", format: "date" },
          acceptance_criteria: { type: "string" },
          definition_of_done: { type: "string" },
          tags: { type: "array", items: { type: "string" } }
        },
        required: ["title", "project"]
      }
    },
    required: ["data"]
  }
};

export async function handleCreateTask(args: any): Promise<Task> {
  const client = new DirectusClient();
  
  try {
    // Validate project exists
    await client.validateProject(args.data.project);
    
    // Get default status for new tasks
    const defaultStatus = await client.getDefaultTaskStatus();
    
    // Prepare task data with defaults
    const taskData: TaskCreateData = {
      ...args.data,
      status: defaultStatus.id,
      progress_percentage: 0,
      ai_generated: args.data.ai_context ? true : false,
      created_at: new Date().toISOString()
    };
    
    // Create task
    const task = await client.createItem('tasks', taskData);
    
    // Handle tags if provided
    if (args.data.tags?.length > 0) {
      await client.manageTags(task.id, args.data.tags);
    }
    
    return task;
    
  } catch (error) {
    throw new Error(`Failed to create task: ${error.message}`);
  }
}

src/tools/task-queries.ts

export const getTaskTool: Tool = {
  name: "mcp__directus__get_task",
  description: "Get detailed task information with optional relations",
  inputSchema: {
    type: "object",
    properties: {
      id: { type: "string", format: "uuid" },
      include_relations: { type: "boolean", default: false }
    },
    required: ["id"]
  }
};

export async function handleGetTask(args: any): Promise<Task> {
  const client = new DirectusClient();
  
  const fields = args.include_relations ? [
    '*',
    'status.*',
    'project.*',
    'assigned_to.*',
    'parent_task.*',
    'dependencies.dependency_task.*',
    'time_entries.*',
    'ai_contexts.*'
  ] : ['*', 'status.*', 'project.name', 'assigned_to.first_name', 'assigned_to.last_name'];
  
  try {
    const task = await client.readItem('tasks', args.id, { fields });
    
    if (!task) {
      throw new Error(`Task with ID ${args.id} not found`);
    }
    
    return task;
    
  } catch (error) {
    throw new Error(`Failed to get task: ${error.message}`);
  }
}

export const listTasksTool: Tool = {
  name: "mcp__directus__list_tasks",
  description: "List tasks with filtering, sorting, and pagination",
  inputSchema: {
    type: "object",
    properties: {
      filters: {
        type: "object",
        properties: {
          project: { type: "string", format: "uuid" },
          status: { type: "string" },
          assigned_to: { type: "string", format: "uuid" },
          priority: { type: "string" },
          task_type: { type: "string" },
          search: { type: "string" },
          due_before: { type: "string", format: "date" },
          created_after: { type: "string", format: "date-time" }
        }
      },
      pagination: {
        type: "object",
        properties: {
          limit: { type: "integer", minimum: 1, maximum: 100, default: 25 },
          offset: { type: "integer", minimum: 0, default: 0 },
          page: { type: "integer", minimum: 1 }
        }
      },
      sort: { 
        type: "array", 
        items: { type: "string" },
        default: ["-created_at"]
      }
    }
  }
};

export async function handleListTasks(args: any): Promise<{ data: Task[], meta: any }> {
  const client = new DirectusClient();
  
  const query: any = {
    fields: ['*', 'status.*', 'project.name', 'assigned_to.first_name', 'assigned_to.last_name'],
    limit: args.pagination?.limit || 25,
    sort: args.sort || ['-created_at']
  };
  
  // Handle pagination
  if (args.pagination?.offset) {
    query.offset = args.pagination.offset;
  } else if (args.pagination?.page) {
    query.offset = (args.pagination.page - 1) * query.limit;
  }
  
  // Build filter conditions
  const filter: any = {};
  
  if (args.filters) {
    if (args.filters.project) filter.project = { _eq: args.filters.project };
    if (args.filters.status) filter.status = { _eq: args.filters.status };
    if (args.filters.assigned_to) filter.assigned_to = { _eq: args.filters.assigned_to };
    if (args.filters.priority) filter.priority = { _eq: args.filters.priority };
    if (args.filters.task_type) filter.task_type = { _eq: args.filters.task_type };
    if (args.filters.due_before) filter.due_date = { _lte: args.filters.due_before };
    if (args.filters.created_after) filter.created_at = { _gte: args.filters.created_after };
    
    // Full-text search
    if (args.filters.search) {
      filter._or = [
        { title: { _icontains: args.filters.search } },
        { description: { _icontains: args.filters.search } }
      ];
    }
  }
  
  if (Object.keys(filter).length > 0) {
    query.filter = filter;
  }
  
  try {
    const result = await client.readItems('tasks', query);
    return result;
    
  } catch (error) {
    throw new Error(`Failed to list tasks: ${error.message}`);
  }
}

Step 3: AI Integration Tools

src/tools/ai-integration.ts

export const createAITaskTool: Tool = {
  name: "mcp__directus__create_ai_task",
  description: "Create a task using AI prompt processing with context awareness",
  inputSchema: {
    type: "object",
    properties: {
      prompt: { type: "string", minLength: 10 },
      context: {
        type: "object",
        properties: {
          project_id: { type: "string", format: "uuid" },
          current_tasks: { type: "array", items: { type: "string" } },
          agent_type: { type: "string" },
          workflow_stage: { type: "string" }
        }
      }
    },
    required: ["prompt"]
  }
};

export async function handleCreateAITask(args: any): Promise<Task> {
  const client = new DirectusClient();
  const aiService = new AITaskProcessor();
  
  try {
    // Get context information
    let projectContext = null;
    if (args.context?.project_id) {
      projectContext = await client.readItem('projects', args.context.project_id);
    }
    
    // Process prompt with AI
    const taskData = await aiService.processTaskPrompt(args.prompt, {
      project: projectContext,
      existingTasks: args.context?.current_tasks || [],
      agentType: args.context?.agent_type,
      workflowStage: args.context?.workflow_stage
    });
    
    // Enhance with AI context
    taskData.ai_generated = true;
    taskData.ai_context = {
      original_prompt: args.prompt,
      processing_timestamp: new Date().toISOString(),
      agent_type: args.context?.agent_type,
      confidence_score: taskData.confidence_score || 0.8
    };
    
    // Create task using standard creation flow
    return await handleCreateTask({ data: taskData });
    
  } catch (error) {
    throw new Error(`Failed to create AI task: ${error.message}`);
  }
}

export const updateTaskAIContextTool: Tool = {
  name: "mcp__directus__update_task_ai_context",
  description: "Update task AI context with agent activity and results",
  inputSchema: {
    type: "object",
    properties: {
      task_id: { type: "string", format: "uuid" },
      context_data: {
        type: "object",
        properties: {
          agent_type: { type: "string" },
          operation: { type: "string" },
          result: { type: "string" },
          files_modified: { type: "array", items: { type: "string" } },
          time_spent: { type: "number" },
          success: { type: "boolean" },
          next_steps: { type: "string" }
        },
        required: ["agent_type", "operation", "success"]
      }
    },
    required: ["task_id", "context_data"]
  }
};

export async function handleUpdateTaskAIContext(args: any): Promise<void> {
  const client = new DirectusClient();
  
  try {
    // Create AI context entry
    await client.createItem('task_ai_contexts', {
      task: args.task_id,
      ai_agent_type: args.context_data.agent_type,
      context_type: 'feedback',
      context_data: args.context_data,
      execution_timestamp: new Date().toISOString(),
      success: args.context_data.success
    });
    
    // Update task progress if specified
    if (args.context_data.progress_update) {
      await client.updateItem('tasks', args.task_id, {
        progress_percentage: args.context_data.progress_update,
        updated_at: new Date().toISOString()
      });
    }
    
  } catch (error) {
    throw new Error(`Failed to update task AI context: ${error.message}`);
  }
}

1.3 Frontend Interface Setup

Directus Admin Interface Customization

Step 1: Collection Interface Configuration

collections/tasks.yaml (via API or admin interface)

collection: tasks
meta:
  icon: check_box
  note: "Task management with full project integration"
  display_template: "{{title}} - {{project.name}}"
  
fields:
  title:
    interface: input
    options:
      placeholder: "Enter task title..."
    display_options:
      tabular: true
    required: true
    
  description:
    interface: input-rich-text-html
    options:
      toolbar: ['bold', 'italic', 'underline', 'link', 'code']
      
  status:
    interface: select-dropdown-m2o
    options:
      template: "{{name}}"
    display: labels
    display_options:
      choices:
        - background: "{{color}}"
          foreground: "#ffffff"
          value: "{{id}}"
          text: "{{name}}"
          
  priority:
    interface: select-dropdown
    display: labels
    display_options:
      choices:
        - { text: "Lowest", value: "lowest", background: "#64748b" }
        - { text: "Low", value: "low", background: "#10b981" }
        - { text: "Medium", value: "medium", background: "#f59e0b" }
        - { text: "High", value: "high", background: "#ef4444" }
        - { text: "Highest", value: "highest", background: "#dc2626" }
        
  project:
    interface: select-dropdown-m2o
    options:
      template: "{{name}}"
    required: true
    
  assigned_to:
    interface: select-dropdown-m2o
    options:
      template: "{{first_name}} {{last_name}}"
      
  progress_percentage:
    interface: slider
    options:
      min: 0
      max: 100
      step: 5
    display: progress-bar
    
  estimated_hours:
    interface: input
    options:
      type: number
      step: 0.25
      min: 0
      
  due_date:
    interface: datetime
    options:
      type: date

Step 2: Custom Layout Configuration

layouts/tasks-detail.vue

<template>
  <div class="task-detail-layout">
    <!-- Header Section -->
    <div class="task-header">
      <v-card class="task-title-card">
        <v-card-title>
          <div class="task-title-section">
            <v-field v-model="values.title" :field="titleField" />
            <div class="task-meta">
              <v-field v-model="values.status" :field="statusField" />
              <v-field v-model="values.priority" :field="priorityField" />
            </div>
          </div>
        </v-card-title>
      </v-card>
    </div>
    
    <!-- Main Content Grid -->
    <div class="task-content-grid">
      <!-- Left Column: Task Details -->
      <div class="task-details">
        <v-card>
          <v-card-title>Task Details</v-card-title>
          <v-card-text>
            <v-field v-model="values.description" :field="descriptionField" />
            <v-field v-model="values.acceptance_criteria" :field="acceptanceCriteriaField" />
            <v-field v-model="values.definition_of_done" :field="definitionOfDoneField" />
          </v-card-text>
        </v-card>
      </div>
      
      <!-- Right Column: Metadata -->
      <div class="task-metadata">
        <v-card class="mb-4">
          <v-card-title>Assignment</v-card-title>
          <v-card-text>
            <v-field v-model="values.project" :field="projectField" />
            <v-field v-model="values.assigned_to" :field="assignedToField" />
            <v-field v-model="values.ai_agent_assigned" :field="aiAgentField" />
          </v-card-text>
        </v-card>
        
        <v-card class="mb-4">
          <v-card-title>Planning</v-card-title>
          <v-card-text>
            <v-field v-model="values.estimated_hours" :field="estimatedHoursField" />
            <v-field v-model="values.story_points" :field="storyPointsField" />
            <v-field v-model="values.complexity" :field="complexityField" />
            <v-field v-model="values.due_date" :field="dueDateField" />
          </v-card-text>
        </v-card>
        
        <v-card>
          <v-card-title>Progress</v-card-title>
          <v-card-text>
            <v-field v-model="values.progress_percentage" :field="progressField" />
            <div class="progress-stats">
              <div>Estimated: {{ values.estimated_hours }}h</div>
              <div>Actual: {{ values.actual_hours }}h</div>
            </div>
          </v-card-text>
        </v-card>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TaskDetailLayout',
  props: {
    collection: String,
    primaryKey: [String, Number],
    item: Object,
    fields: Array
  },
  
  computed: {
    values() {
      return this.item || {};
    },
    
    titleField() {
      return this.fields.find(f => f.field === 'title');
    },
    
    statusField() {
      return this.fields.find(f => f.field === 'status');
    },
    
    // ... other field getters
  }
};
</script>

<style scoped>
.task-detail-layout {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}

.task-header {
  margin-bottom: 24px;
}

.task-content-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 24px;
}

.task-title-section {
  width: 100%;
}

.task-meta {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}

.progress-stats {
  margin-top: 8px;
  font-size: 14px;
  color: var(--foreground-subdued);
}

@media (max-width: 768px) {
  .task-content-grid {
    grid-template-columns: 1fr;
  }
}
</style>

Phase 2: Integration Implementation Guide

2.1 Task Master Sync Integration

Bidirectional Sync Service

src/services/task-master-sync.ts

import { EventEmitter } from 'events';
import { spawn } from 'child_process';
import { DirectusClient } from '../utils/directus-client';

export class TaskMasterSyncService extends EventEmitter {
  private directus: DirectusClient;
  private syncInterval: NodeJS.Timer | null = null;
  private isRunning: boolean = false;
  
  constructor() {
    super();
    this.directus = new DirectusClient();
  }
  
  async startSync(intervalMinutes: number = 5): Promise<void> {
    if (this.isRunning) return;
    
    this.isRunning = true;
    console.log('Starting Task Master sync service...');
    
    // Initial sync
    await this.performSync();
    
    // Set up recurring sync
    this.syncInterval = setInterval(async () => {
      try {
        await this.performSync();
      } catch (error) {
        console.error('Sync error:', error);
        this.emit('sync-error', error);
      }
    }, intervalMinutes * 60 * 1000);
    
    this.emit('sync-started');
  }
  
  async stopSync(): Promise<void> {
    if (this.syncInterval) {
      clearInterval(this.syncInterval);
      this.syncInterval = null;
    }
    this.isRunning = false;
    this.emit('sync-stopped');
  }
  
  async performSync(): Promise<void> {
    console.log('Starting bidirectional sync...');
    const syncResult = {
      timestamp: new Date().toISOString(),
      taskMasterToDirectus: 0,
      directusToTaskMaster: 0,
      conflicts: 0,
      errors: []
    };
    
    try {
      // Phase 1: Task Master → Directus
      const tmUpdates = await this.syncFromTaskMaster();
      syncResult.taskMasterToDirectus = tmUpdates.length;
      
      // Phase 2: Directus → Task Master  
      const directusUpdates = await this.syncToTaskMaster();
      syncResult.directusToTaskMaster = directusUpdates.length;
      
      // Phase 3: Conflict Resolution
      const conflicts = await this.resolveConflicts();
      syncResult.conflicts = conflicts.length;
      
      console.log('Sync completed:', syncResult);
      this.emit('sync-completed', syncResult);
      
    } catch (error) {
      syncResult.errors.push(error.message);
      this.emit('sync-error', error);
      throw error;
    }
  }
  
  private async syncFromTaskMaster(): Promise<any[]> {
    // Export from Task Master
    const tmData = await this.executeTaskMasterCommand(['export', '--format=json', '--since-sync']);
    
    if (!tmData.tasks || tmData.tasks.length === 0) {
      return [];
    }
    
    const updates: any[] = [];
    
    for (const tmTask of tmData.tasks) {
      try {
        // Check if task exists in Directus
        const existingTask = await this.directus.readItems('tasks', {
          filter: { task_master_id: { _eq: tmTask.id } },
          limit: 1
        });
        
        if (existingTask.data.length > 0) {
          // Update existing task
          const directusTask = existingTask.data[0];
          const updateData = this.mapTaskMasterToDirectus(tmTask);
          
          // Check for conflicts
          if (this.hasConflict(directusTask, updateData, tmTask)) {
            await this.recordConflict(directusTask.id, tmTask, updateData);
            continue;
          }
          
          await this.directus.updateItem('tasks', directusTask.id, updateData);
          updates.push({ type: 'update', taskMasterId: tmTask.id, directusId: directusTask.id });
          
        } else {
          // Create new task
          const newTaskData = this.mapTaskMasterToDirectus(tmTask);
          newTaskData.task_master_id = tmTask.id;
          
          const newTask = await this.directus.createItem('tasks', newTaskData);
          updates.push({ type: 'create', taskMasterId: tmTask.id, directusId: newTask.id });
        }
        
      } catch (error) {
        console.error(`Error syncing task ${tmTask.id}:`, error);
        continue;
      }
    }
    
    return updates;
  }
  
  private async syncToTaskMaster(): Promise<any[]> {
    // Get tasks modified since last sync
    const lastSync = await this.getLastSyncTimestamp();
    const modifiedTasks = await this.directus.readItems('tasks', {
      filter: {
        _and: [
          { updated_at: { _gt: lastSync } },
          { task_master_id: { _nnull: true } }
        ]
      }
    });
    
    const updates: any[] = [];
    
    for (const directusTask of modifiedTasks.data) {
      try {
        const tmData = this.mapDirectusToTaskMaster(directusTask);
        
        // Update in Task Master
        await this.executeTaskMasterCommand([
          'update-task',
          '--id', directusTask.task_master_id,
          '--data', JSON.stringify(tmData)
        ]);
        
        updates.push({ 
          type: 'update', 
          directusId: directusTask.id, 
          taskMasterId: directusTask.task_master_id 
        });
        
      } catch (error) {
        console.error(`Error syncing to Task Master ${directusTask.task_master_id}:`, error);
        continue;
      }
    }
    
    return updates;
  }
  
  private mapTaskMasterToDirectus(tmTask: any): any {
    return {
      title: tmTask.title,
      description: tmTask.description,
      // Map Task Master status to Directus status
      status: this.mapStatus(tmTask.status, 'tm_to_directus'),
      priority: this.mapPriority(tmTask.priority, 'tm_to_directus'),
      estimated_hours: tmTask.estimated_hours,
      progress_percentage: tmTask.progress || 0,
      due_date: tmTask.due_date,
      acceptance_criteria: tmTask.acceptance_criteria,
      definition_of_done: tmTask.definition_of_done,
      updated_at: new Date().toISOString(),
      // Preserve sync metadata
      external_refs: {
        ...tmTask.external_refs,
        last_tm_sync: new Date().toISOString()
      }
    };
  }
  
  private mapDirectusToTaskMaster(directusTask: any): any {
    return {
      title: directusTask.title,
      description: directusTask.description,
      status: this.mapStatus(directusTask.status.slug, 'directus_to_tm'),
      priority: this.mapPriority(directusTask.priority, 'directus_to_tm'),
      estimated_hours: directusTask.estimated_hours,
      progress: directusTask.progress_percentage,
      due_date: directusTask.due_date,
      acceptance_criteria: directusTask.acceptance_criteria,
      definition_of_done: directusTask.definition_of_done
    };
  }
  
  private async executeTaskMasterCommand(args: string[]): Promise<any> {
    return new Promise((resolve, reject) => {
      const process = spawn('task-master', args, { stdio: 'pipe' });
      let output = '';
      
      process.stdout.on('data', (data) => {
        output += data.toString();
      });
      
      process.on('close', (code) => {
        if (code === 0) {
          try {
            resolve(JSON.parse(output));
          } catch (error) {
            resolve({ output });
          }
        } else {
          reject(new Error(`Task Master command failed with code ${code}`));
        }
      });
    });
  }
  
  private hasConflict(directusTask: any, updateData: any, tmTask: any): boolean {
    // Simple timestamp-based conflict detection
    const directusModified = new Date(directusTask.updated_at);
    const tmModified = new Date(tmTask.updated_at);
    const lastSync = new Date(directusTask.external_refs?.last_tm_sync || 0);
    
    // Conflict if both have been modified since last sync
    return directusModified > lastSync && tmModified > lastSync;
  }
  
  private async recordConflict(directusTaskId: string, tmTask: any, updateData: any): Promise<void> {
    await this.directus.createItem('task_sync_conflicts', {
      task: directusTaskId,
      conflict_type: 'bidirectional_update',
      directus_data: updateData,
      task_master_data: tmTask,
      detected_at: new Date().toISOString(),
      resolution_status: 'pending'
    });
  }
  
  private mapStatus(status: string, direction: 'tm_to_directus' | 'directus_to_tm'): string {
    const statusMap = {
      tm_to_directus: {
        'pending': 'todo',
        'in-progress': 'in_progress', 
        'done': 'completed',
        'blocked': 'blocked'
      },
      directus_to_tm: {
        'todo': 'pending',
        'in_progress': 'in-progress',
        'completed': 'done',
        'blocked': 'blocked'
      }
    };
    
    return statusMap[direction][status] || status;
  }
  
  private async getLastSyncTimestamp(): Promise<string> {
    const syncRecord = await this.directus.readItems('sync_metadata', {
      filter: { sync_type: { _eq: 'task_master' } },
      sort: ['-created_at'],
      limit: 1
    });
    
    return syncRecord.data[0]?.last_sync_time || new Date(0).toISOString();
  }
}

2.2 BMad Integration Implementation

BMad Workflow Templates

src/services/bmad-integration.ts

export class BMadIntegrationService {
  private directus: DirectusClient;
  
  constructor() {
    this.directus = new DirectusClient();
  }
  
  async createBMadEpic(epicData: BMadEpicData): Promise<Task> {
    // Create epic task
    const epic = await this.directus.createItem('tasks', {
      title: epicData.title,
      description: epicData.description,
      task_type: 'feature',
      priority: 'high',
      complexity: 'major',
      project: epicData.project_id,
      bmad_story_id: epicData.bmad_id,
      ai_generated: true,
      ai_context: {
        bmad_workflow: 'epic_creation',
        planning_phase: epicData.planning_phase,
        methodology: 'bmad'
      }
    });
    
    // Create workflow tracking entries
    await this.initializeBMadWorkflow(epic.id, epicData.workflow_type);
    
    return epic;
  }
  
  async generateUserStories(epicId: string, storyCount: number = 3): Promise<Task[]> {
    const epic = await this.directus.readItem('tasks', epicId);
    if (!epic) throw new Error('Epic not found');
    
    const stories: Task[] = [];
    
    for (let i = 1; i <= storyCount; i++) {
      const story = await this.directus.createItem('tasks', {
        title: `${epic.title} - User Story ${i}`,
        description: `User story derived from epic: ${epic.title}`,
        task_type: 'feature',
        priority: epic.priority,
        complexity: 'minor',
        project: epic.project,
        parent_task: epic.id,
        epic: epic.id,
        bmad_story_id: `${epic.bmad_story_id}-story-${i}`,
        ai_generated: true,
        ai_context: {
          bmad_workflow: 'story_generation',
          parent_epic: epic.id,
          story_number: i
        }
      });
      
      stories.push(story);
    }
    
    return stories;
  }
  
  private async initializeBMadWorkflow(taskId: string, workflowType: string): Promise<void> {
    const workflows = this.getBMadWorkflowSteps(workflowType);
    
    for (const [index, step] of workflows.entries()) {
      await this.directus.createItem('task_bmad_workflows', {
        task: taskId,
        bmad_phase: step.phase,
        bmad_agent: step.agent,
        workflow_step: step.step,
        step_status: 'pending',
        execution_order: index + 1,
        step_data: step.default_data || {}
      });
    }
  }
  
  private getBMadWorkflowSteps(workflowType: string): BMadWorkflowStep[] {
    const workflows = {
      greenfield: [
        {
          phase: 'analysis',
          agent: 'pm',
          step: 'requirements_gathering',
          default_data: { templates: ['prd', 'user_stories'] }
        },
        {
          phase: 'analysis', 
          agent: 'architect',
          step: 'system_design',
          default_data: { deliverables: ['architecture_diagram', 'tech_stack'] }
        },
        {
          phase: 'planning',
          agent: 'analyst',
          step: 'research_analysis', 
          default_data: { research_areas: ['market', 'technical', 'competitive'] }
        },
        {
          phase: 'development',
          agent: 'dev',
          step: 'implementation',
          default_data: { coding_standards: true, tdd_required: true }
        },
        {
          phase: 'review',
          agent: 'qa',
          step: 'quality_assurance',
          default_data: { test_coverage_min: 80, security_review: true }
        }
      ],
      
      brownfield: [
        {
          phase: 'analysis',
          agent: 'architect',
          step: 'system_assessment',
          default_data: { legacy_analysis: true, impact_assessment: true }
        },
        {
          phase: 'planning',
          agent: 'pm',
          step: 'enhancement_planning',
          default_data: { risk_assessment: true, rollback_plan: true }
        },
        {
          phase: 'development',
          agent: 'dev', 
          step: 'incremental_implementation',
          default_data: { backwards_compatibility: true, feature_flags: true }
        },
        {
          phase: 'review',
          agent: 'qa',
          step: 'regression_testing',
          default_data: { full_regression: true, performance_testing: true }
        }
      ]
    };
    
    return workflows[workflowType] || workflows.greenfield;
  }
  
  async updateWorkflowStep(taskId: string, stepId: string, stepData: any): Promise<void> {
    await this.directus.updateItem('task_bmad_workflows', stepId, {
      step_status: stepData.status,
      step_data: { ...stepData.data },
      completed_at: stepData.status === 'completed' ? new Date().toISOString() : null
    });
    
    // Check if all steps are complete
    if (stepData.status === 'completed') {
      const allSteps = await this.directus.readItems('task_bmad_workflows', {
        filter: { task: { _eq: taskId } }
      });
      
      const allComplete = allSteps.data.every(step => step.step_status === 'completed');
      
      if (allComplete) {
        await this.directus.updateItem('tasks', taskId, {
          status: await this.getStatusId('completed'),
          progress_percentage: 100,
          completed_at: new Date().toISOString()
        });
      }
    }
  }
}

This implementation guide provides comprehensive code examples and setup instructions for the first two phases of the Directus Task Management Suite. The remaining phases would follow similar patterns with additional features like analytics dashboards, advanced AI integration, and performance optimizations.

The key to successful implementation is:

  1. Start with solid foundations - proper database design and API patterns
  2. Build incrementally - each phase delivers working functionality
  3. Test thoroughly - especially integration points with existing systems
  4. Document extensively - enable future maintenance and enhancement
  5. Monitor performance - ensure scalability as usage grows

Each code example includes error handling, validation, and integration patterns that align with the existing project architecture and established development practices.