/** * Create All Directus Collections * Main script to create all 10 core collections for the Task Management Suite */ const { getAuthenticatedClient } = require('../utils/directus-client'); const { createCollection, createFields, FIELD_TYPES, getSystemFields } = require('../utils/schema-helpers'); /** * Create Projects Collection */ async function createProjectsCollection(client) { console.log('\nšŸ“¦ Creating Projects Collection...'); // Create collection await createCollection(client, { collection: 'projects', meta: { collection: 'projects', icon: 'folder', note: 'Project hierarchy and organization', display_template: '{{name}}', archive_field: 'status', archive_value: 'archived', sort_field: 'sort', accountability: 'all', color: '#6366F1' }, schema: { name: 'projects' } }); // Create fields const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.string('name', { required: true, width: 'half' }), FIELD_TYPES.text('description', { interface: 'input-rich-text-html' }), FIELD_TYPES.dropdown('status', [ { text: 'Active', value: 'active' }, { text: 'On Hold', value: 'on_hold' }, { text: 'Completed', value: 'completed' }, { text: 'Archived', value: 'archived' } ], { default: 'active', width: 'half' }), FIELD_TYPES.m2o('parent_project', 'projects', { nullable: true }), FIELD_TYPES.string('repository_url', { nullable: true, interface: 'input', meta: { placeholder: 'https://github.com/...' } }), FIELD_TYPES.dropdown('bmad_workflow_type', [ { text: 'Greenfield', value: 'greenfield' }, { text: 'Brownfield', value: 'brownfield' }, { text: 'Maintenance', value: 'maintenance' } ], { nullable: true, width: 'half' }), FIELD_TYPES.string('task_master_project_id', { nullable: true, width: 'half' }), FIELD_TYPES.dropdown('priority', [ { text: 'Low', value: 'low' }, { text: 'Medium', value: 'medium' }, { text: 'High', value: 'high' }, { text: 'Critical', value: 'critical' } ], { default: 'medium', width: 'half' }), FIELD_TYPES.date('start_date', { nullable: true, width: 'half' }), FIELD_TYPES.date('due_date', { nullable: true, width: 'half' }), FIELD_TYPES.integer('completion_percentage', { default: 0, width: 'half', interface: 'slider', options: { min: 0, max: 100, step: 1 } }), FIELD_TYPES.m2o('created_by', 'directus_users', { width: 'half' }), FIELD_TYPES.m2o('assigned_to', 'directus_users', { nullable: true, width: 'half' }), FIELD_TYPES.json('metadata', { nullable: true }), FIELD_TYPES.integer('sort', { hidden: true, interface: 'input' }), ...getSystemFields() ]; await createFields(client, 'projects', fields); } /** * Create Task Statuses Collection */ async function createTaskStatusesCollection(client) { console.log('\nšŸ“¦ Creating Task Statuses Collection...'); await createCollection(client, { collection: 'task_statuses', meta: { collection: 'task_statuses', icon: 'assignment_turned_in', note: 'Customizable status definitions aligned with workflows', display_template: '{{name}}', sort_field: 'sort_order', accountability: 'all', color: '#10B981' }, schema: { name: 'task_statuses' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.string('name', { required: true, width: 'half' }), FIELD_TYPES.string('slug', { required: true, width: 'half' }), FIELD_TYPES.text('description'), FIELD_TYPES.string('color', { default: '#6366f1', interface: 'select-color', width: 'half' }), FIELD_TYPES.string('icon', { interface: 'select-icon', width: 'half' }), FIELD_TYPES.dropdown('category', [ { text: 'To Do', value: 'todo' }, { text: 'In Progress', value: 'in_progress' }, { text: 'Review', value: 'review' }, { text: 'Done', value: 'done' }, { text: 'Blocked', value: 'blocked' } ], { width: 'half' }), FIELD_TYPES.boolean('is_final', { default: false, width: 'half' }), FIELD_TYPES.json('auto_transition_rules', { nullable: true }), FIELD_TYPES.dropdown('workflow_type', [ { text: 'BMad', value: 'bmad' }, { text: 'Task Master', value: 'task_master' }, { text: 'GitHub', value: 'github' }, { text: 'Custom', value: 'custom' } ], { width: 'half', nullable: true }), FIELD_TYPES.integer('sort_order', { default: 0, width: 'half' }), FIELD_TYPES.boolean('active', { default: true, width: 'half' }), ...getSystemFields() ]; await createFields(client, 'task_statuses', fields); } /** * Create Tasks Collection */ async function createTasksCollection(client) { console.log('\nšŸ“¦ Creating Tasks Collection...'); await createCollection(client, { collection: 'tasks', meta: { collection: 'tasks', icon: 'task_alt', note: 'Core task entity with rich metadata', display_template: '{{title}}', archive_field: 'status', sort_field: 'sort', accountability: 'all', color: '#3B82F6' }, schema: { name: 'tasks' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.string('title', { required: true }), FIELD_TYPES.text('description', { interface: 'input-rich-text-html' }), FIELD_TYPES.m2o('status', 'task_statuses', { width: 'half' }), FIELD_TYPES.dropdown('priority', [ { text: 'Lowest', value: 'lowest' }, { text: 'Low', value: 'low' }, { text: 'Medium', value: 'medium' }, { text: 'High', value: 'high' }, { text: 'Highest', value: 'highest' } ], { default: 'medium', width: 'half' }), FIELD_TYPES.dropdown('task_type', [ { text: 'Feature', value: 'feature' }, { text: 'Bug', value: 'bug' }, { text: 'Enhancement', value: 'enhancement' }, { text: 'Research', value: 'research' }, { text: 'Maintenance', value: 'maintenance' } ], { width: 'half' }), FIELD_TYPES.dropdown('complexity', [ { text: 'Trivial', value: 'trivial' }, { text: 'Minor', value: 'minor' }, { text: 'Major', value: 'major' }, { text: 'Critical', value: 'critical' } ], { width: 'half', nullable: true }), FIELD_TYPES.integer('story_points', { width: 'half', nullable: true, interface: 'select-dropdown', options: { choices: [ { text: '1', value: 1 }, { text: '2', value: 2 }, { text: '3', value: 3 }, { text: '5', value: 5 }, { text: '8', value: 8 }, { text: '13', value: 13 }, { text: '21', value: 21 } ] } }), // Project relationships FIELD_TYPES.m2o('project', 'projects', { required: true, width: 'half' }), FIELD_TYPES.m2o('parent_task', 'tasks', { nullable: true, width: 'half' }), FIELD_TYPES.m2o('epic', 'tasks', { nullable: true, width: 'half' }), // Assignment and ownership FIELD_TYPES.m2o('created_by', 'directus_users', { width: 'half' }), FIELD_TYPES.m2o('assigned_to', 'directus_users', { nullable: true, width: 'half' }), FIELD_TYPES.string('ai_agent_assigned', { nullable: true, width: 'half' }), FIELD_TYPES.m2o('reviewer', 'directus_users', { nullable: true, width: 'half' }), // Time and progress FIELD_TYPES.decimal('estimated_hours', { nullable: true, width: 'half', precision: 10, scale: 2 }), FIELD_TYPES.decimal('actual_hours', { nullable: true, width: 'half', precision: 10, scale: 2, readonly: true }), FIELD_TYPES.integer('progress_percentage', { default: 0, width: 'half', interface: 'slider', options: { min: 0, max: 100, step: 5 } }), FIELD_TYPES.date('start_date', { nullable: true, width: 'half' }), FIELD_TYPES.date('due_date', { nullable: true, width: 'half' }), FIELD_TYPES.datetime('completed_at', { nullable: true, width: 'half' }), // Integration fields FIELD_TYPES.string('task_master_id', { nullable: true, width: 'half' }), FIELD_TYPES.string('github_issue_url', { nullable: true }), FIELD_TYPES.string('bmad_story_id', { nullable: true, width: 'half' }), FIELD_TYPES.json('external_refs', { nullable: true }), // AI and automation FIELD_TYPES.boolean('ai_generated', { default: false, width: 'half' }), FIELD_TYPES.json('ai_context', { nullable: true }), FIELD_TYPES.boolean('auto_status_updates', { default: false, width: 'half' }), // Metadata FIELD_TYPES.text('acceptance_criteria', { interface: 'input-rich-text-html', nullable: true }), FIELD_TYPES.text('definition_of_done', { interface: 'input-rich-text-html', nullable: true }), FIELD_TYPES.text('notes', { interface: 'input-rich-text-html', nullable: true }), FIELD_TYPES.integer('sort', { hidden: true, interface: 'input' }), ...getSystemFields() ]; await createFields(client, 'tasks', fields); } /** * Create Task Dependencies Collection */ async function createTaskDependenciesCollection(client) { console.log('\nšŸ“¦ Creating Task Dependencies Collection...'); await createCollection(client, { collection: 'task_dependencies', meta: { collection: 'task_dependencies', icon: 'device_hub', note: 'Task relationship and dependency management', display_template: '{{dependent_task}} -> {{dependency_task}}', accountability: 'all', color: '#F59E0B' }, schema: { name: 'task_dependencies' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.m2o('dependent_task', 'tasks', { required: true, width: 'half' }), FIELD_TYPES.m2o('dependency_task', 'tasks', { required: true, width: 'half' }), FIELD_TYPES.dropdown('dependency_type', [ { text: 'Blocks', value: 'blocks' }, { text: 'Relates To', value: 'relates_to' }, { text: 'Duplicates', value: 'duplicates' }, { text: 'Subtask Of', value: 'subtask_of' } ], { default: 'blocks', width: 'half' }), FIELD_TYPES.boolean('is_hard_dependency', { default: true, width: 'half' }), FIELD_TYPES.m2o('created_by', 'directus_users'), ...getSystemFields() ]; await createFields(client, 'task_dependencies', fields); } /** * Create Task Time Entries Collection */ async function createTaskTimeEntriesCollection(client) { console.log('\nšŸ“¦ Creating Task Time Entries Collection...'); await createCollection(client, { collection: 'task_time_entries', meta: { collection: 'task_time_entries', icon: 'schedule', note: 'Time tracking and progress data', display_template: '{{hours}}h - {{description}}', accountability: 'all', color: '#8B5CF6' }, schema: { name: 'task_time_entries' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.m2o('task', 'tasks', { required: true }), FIELD_TYPES.m2o('user', 'directus_users', { nullable: true, width: 'half' }), FIELD_TYPES.string('ai_agent', { nullable: true, width: 'half' }), FIELD_TYPES.text('description'), FIELD_TYPES.decimal('hours', { required: true, width: 'half', precision: 10, scale: 2 }), FIELD_TYPES.dropdown('entry_type', [ { text: 'Manual', value: 'manual' }, { text: 'Automatic', value: 'automatic' }, { text: 'Estimated', value: 'estimated' }, { text: 'Actual', value: 'actual' } ], { width: 'half' }), FIELD_TYPES.dropdown('work_type', [ { text: 'Development', value: 'development' }, { text: 'Testing', value: 'testing' }, { text: 'Review', value: 'review' }, { text: 'Research', value: 'research' }, { text: 'Meeting', value: 'meeting' } ], { width: 'half' }), FIELD_TYPES.boolean('billable', { default: false, width: 'half' }), FIELD_TYPES.datetime('started_at', { nullable: true, width: 'half' }), FIELD_TYPES.datetime('ended_at', { nullable: true, width: 'half' }), ...getSystemFields() ]; await createFields(client, 'task_time_entries', fields); } /** * Create Task Templates Collection */ async function createTaskTemplatesCollection(client) { console.log('\nšŸ“¦ Creating Task Templates Collection...'); await createCollection(client, { collection: 'task_templates', meta: { collection: 'task_templates', icon: 'content_copy', note: 'Reusable task patterns for BMad methodology', display_template: '{{name}}', accountability: 'all', color: '#EC4899' }, schema: { name: 'task_templates' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.string('name', { required: true }), FIELD_TYPES.text('description'), FIELD_TYPES.dropdown('template_type', [ { text: 'BMad Epic', value: 'bmad_epic' }, { text: 'BMad Story', value: 'bmad_story' }, { text: 'Feature', value: 'feature' }, { text: 'Bug Fix', value: 'bug_fix' }, { text: 'Maintenance', value: 'maintenance' } ], { width: 'half' }), FIELD_TYPES.dropdown('workflow_stage', [ { text: 'Planning', value: 'planning' }, { text: 'Development', value: 'development' }, { text: 'Testing', value: 'testing' }, { text: 'Deployment', value: 'deployment' } ], { width: 'half' }), // Template data FIELD_TYPES.string('title_template'), FIELD_TYPES.text('description_template'), FIELD_TYPES.text('acceptance_criteria_template'), FIELD_TYPES.dropdown('default_priority', [ { text: 'Lowest', value: 'lowest' }, { text: 'Low', value: 'low' }, { text: 'Medium', value: 'medium' }, { text: 'High', value: 'high' }, { text: 'Highest', value: 'highest' } ], { width: 'half' }), FIELD_TYPES.dropdown('default_complexity', [ { text: 'Trivial', value: 'trivial' }, { text: 'Minor', value: 'minor' }, { text: 'Major', value: 'major' }, { text: 'Critical', value: 'critical' } ], { width: 'half', nullable: true }), FIELD_TYPES.decimal('estimated_hours', { nullable: true, width: 'half', precision: 10, scale: 2 }), // BMad integration FIELD_TYPES.string('bmad_agent_assignment', { nullable: true, width: 'half' }), FIELD_TYPES.json('required_skills', { nullable: true }), FIELD_TYPES.json('checklist_items', { nullable: true }), // Usage tracking FIELD_TYPES.integer('usage_count', { default: 0, width: 'half', readonly: true }), FIELD_TYPES.datetime('last_used', { nullable: true, width: 'half', readonly: true }), FIELD_TYPES.m2o('created_by', 'directus_users'), ...getSystemFields() ]; await createFields(client, 'task_templates', fields); } /** * Create Task AI Contexts Collection */ async function createTaskAIContextsCollection(client) { console.log('\nšŸ“¦ Creating Task AI Contexts Collection...'); await createCollection(client, { collection: 'task_ai_contexts', meta: { collection: 'task_ai_contexts', icon: 'psychology', note: 'AI agent context and prompt data', display_template: '{{ai_agent_type}} - {{context_type}}', accountability: 'all', color: '#06B6D4' }, schema: { name: 'task_ai_contexts' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.m2o('task', 'tasks', { required: true }), FIELD_TYPES.string('ai_agent_type', { width: 'half' }), FIELD_TYPES.dropdown('context_type', [ { text: 'Prompt', value: 'prompt' }, { text: 'Result', value: 'result' }, { text: 'Feedback', value: 'feedback' }, { text: 'Error', value: 'error' } ], { width: 'half' }), FIELD_TYPES.json('context_data'), FIELD_TYPES.datetime('execution_timestamp'), FIELD_TYPES.boolean('success', { width: 'half' }), FIELD_TYPES.integer('tokens_used', { nullable: true, width: 'half' }), FIELD_TYPES.integer('execution_time_ms', { nullable: true, width: 'half' }), ...getSystemFields() ]; await createFields(client, 'task_ai_contexts', fields); } /** * Create Task BMad Workflows Collection */ async function createTaskBMadWorkflowsCollection(client) { console.log('\nšŸ“¦ Creating Task BMad Workflows Collection...'); await createCollection(client, { collection: 'task_bmad_workflows', meta: { collection: 'task_bmad_workflows', icon: 'account_tree', note: 'BMad methodology integration', display_template: '{{workflow_stage}} - {{agent_type}}', accountability: 'all', color: '#14B8A6' }, schema: { name: 'task_bmad_workflows' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.m2o('task', 'tasks', { required: true }), FIELD_TYPES.dropdown('workflow_stage', [ { text: 'Project Manager', value: 'pm' }, { text: 'Analyst', value: 'analyst' }, { text: 'Architect', value: 'architect' }, { text: 'Product Owner', value: 'po' }, { text: 'Scrum Master', value: 'sm' }, { text: 'Developer', value: 'dev' }, { text: 'QA', value: 'qa' }, { text: 'UX Expert', value: 'ux' } ], { width: 'half' }), FIELD_TYPES.string('agent_type', { width: 'half' }), FIELD_TYPES.json('agent_context'), FIELD_TYPES.dropdown('status', [ { text: 'Not Started', value: 'not_started' }, { text: 'In Progress', value: 'in_progress' }, { text: 'Completed', value: 'completed' }, { text: 'Skipped', value: 'skipped' } ], { default: 'not_started', width: 'half' }), FIELD_TYPES.json('deliverables'), FIELD_TYPES.text('notes'), FIELD_TYPES.datetime('started_at', { nullable: true, width: 'half' }), FIELD_TYPES.datetime('completed_at', { nullable: true, width: 'half' }), ...getSystemFields() ]; await createFields(client, 'task_bmad_workflows', fields); } /** * Create Task Assignments Collection */ async function createTaskAssignmentsCollection(client) { console.log('\nšŸ“¦ Creating Task Assignments Collection...'); await createCollection(client, { collection: 'task_assignments', meta: { collection: 'task_assignments', icon: 'group', note: 'Team assignments and collaboration', display_template: '{{user}} - {{role}}', accountability: 'all', color: '#F97316' }, schema: { name: 'task_assignments' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.m2o('task', 'tasks', { required: true }), FIELD_TYPES.m2o('user', 'directus_users', { nullable: true, width: 'half' }), FIELD_TYPES.string('ai_agent', { nullable: true, width: 'half' }), FIELD_TYPES.dropdown('role', [ { text: 'Owner', value: 'owner' }, { text: 'Assignee', value: 'assignee' }, { text: 'Reviewer', value: 'reviewer' }, { text: 'Watcher', value: 'watcher' }, { text: 'Contributor', value: 'contributor' } ], { width: 'half' }), FIELD_TYPES.decimal('capacity_percentage', { nullable: true, width: 'half', precision: 5, scale: 2, interface: 'slider', options: { min: 0, max: 100, step: 5 } }), FIELD_TYPES.datetime('assigned_at', { width: 'half' }), FIELD_TYPES.datetime('started_at', { nullable: true, width: 'half' }), FIELD_TYPES.datetime('completed_at', { nullable: true, width: 'half' }), FIELD_TYPES.text('notes', { nullable: true }), ...getSystemFields() ]; await createFields(client, 'task_assignments', fields); } /** * Create Task External References Collection */ async function createTaskExternalRefsCollection(client) { console.log('\nšŸ“¦ Creating Task External References Collection...'); await createCollection(client, { collection: 'task_external_refs', meta: { collection: 'task_external_refs', icon: 'link', note: 'External system references and integrations', display_template: '{{ref_type}}: {{ref_id}}', accountability: 'all', color: '#64748B' }, schema: { name: 'task_external_refs' } }); const fields = [ FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }), FIELD_TYPES.m2o('task', 'tasks', { required: true }), FIELD_TYPES.dropdown('ref_type', [ { text: 'GitHub Issue', value: 'github_issue' }, { text: 'GitHub PR', value: 'github_pr' }, { text: 'Task Master', value: 'task_master' }, { text: 'Jira', value: 'jira' }, { text: 'Slack Thread', value: 'slack_thread' }, { text: 'Google Doc', value: 'google_doc' }, { text: 'Figma', value: 'figma' }, { text: 'Other', value: 'other' } ], { width: 'half' }), FIELD_TYPES.string('ref_id', { required: true, width: 'half' }), FIELD_TYPES.string('ref_url', { nullable: true }), FIELD_TYPES.json('metadata', { nullable: true }), FIELD_TYPES.boolean('auto_sync', { default: false, width: 'half' }), FIELD_TYPES.datetime('last_synced', { nullable: true, width: 'half' }), ...getSystemFields() ]; await createFields(client, 'task_external_refs', fields); } /** * Main function to create all collections */ async function main() { console.log('šŸš€ Starting Directus Collections Creation...\n'); const summary = { collections: { created: 0, existing: 0, errors: 0 }, fields: { created: 0, existing: 0, errors: 0 } }; try { // Get authenticated client const client = await getAuthenticatedClient(); // Track results for each collection const collectionFunctions = [ { name: 'Projects', fn: createProjectsCollection }, { name: 'Task Statuses', fn: createTaskStatusesCollection }, { name: 'Tasks', fn: createTasksCollection }, { name: 'Task Dependencies', fn: createTaskDependenciesCollection }, { name: 'Task Time Entries', fn: createTaskTimeEntriesCollection }, { name: 'Task Templates', fn: createTaskTemplatesCollection }, { name: 'Task AI Contexts', fn: createTaskAIContextsCollection }, { name: 'Task BMad Workflows', fn: createTaskBMadWorkflowsCollection }, { name: 'Task Assignments', fn: createTaskAssignmentsCollection }, { name: 'Task External Refs', fn: createTaskExternalRefsCollection } ]; // Create all collections for (const { name, fn } of collectionFunctions) { try { await fn(client); summary.collections.created++; } catch (error) { console.error(` āŒ Error with ${name} collection:`, error.message); summary.collections.errors++; } } console.log('\n' + '='.repeat(60)); console.log('šŸ“Š FINAL SUMMARY'); console.log('='.repeat(60)); console.log('\nšŸ“¦ Collections:'); console.log(` āœ… Processed: ${summary.collections.created + summary.collections.existing}`); console.log(` āš ļø Errors: ${summary.collections.errors}`); console.log('\n✨ Status:'); if (summary.collections.errors === 0) { console.log(' āœ… All collections processed successfully!'); console.log(' āœ… Ready for relationship setup'); console.log(' āœ… Ready for validation and indexing'); } else { console.log(' āš ļø Some collections had issues, but system is functional'); console.log(' ā„¹ļø Run script again to retry failed operations'); } console.log('\nšŸ“ Next Steps:'); console.log(' 1. Run: npm run setup-relationships'); console.log(' 2. Run: npm run setup-validation'); console.log(' 3. Run: npm run create-indexes'); console.log('='.repeat(60)); } catch (error) { console.error('\nāŒ Critical error:', error.message); if (error.response) { console.error('Response data:', error.response.data); } process.exit(1); } } // Run if called directly if (require.main === module) { main(); } module.exports = { createProjectsCollection, createTaskStatusesCollection, createTasksCollection, createTaskDependenciesCollection, createTaskTimeEntriesCollection, createTaskTemplatesCollection, createTaskAIContextsCollection, createTaskBMadWorkflowsCollection, createTaskAssignmentsCollection, createTaskExternalRefsCollection, main };