UNPKG

oneie

Version:

šŸ¤ ONE Personal Collaborative Intelligence - Creates personalized AI workspace from your me.md profile. Simple: npx oneie → edit me.md → generate personalized agents, workflows & missions. From students to enterprises, ONE adapts to your context.

717 lines (587 loc) • 25.3 kB
#!/usr/bin/env node /** * Task Orchestration System * Manages tasks within stories and coordinates agent assignments */ import fs from 'fs-extra'; import path from 'path'; import yaml from 'js-yaml'; import chalk from 'chalk'; import inquirer from 'inquirer'; export class TaskSystem { constructor(projectPath = process.cwd()) { this.projectPath = projectPath; this.oneDir = path.join(projectPath, '.one'); this.missionsDir = path.join(this.oneDir, 'missions'); this.agentsDir = path.join(projectPath, '.claude', 'agents'); // Agent registry from your existing system this.agents = { // Command structure agents 'Mission Commander': { file: 'mission-commander.md', specialty: 'strategic planning, mission creation' }, 'Story Teller': { file: 'story-teller.md', specialty: 'story creation, narrative development' }, 'Task Master': { file: 'task-master.md', specialty: 'task execution, agent coordination' }, // Engineering team 'Engineering Director': { file: 'engineering-director.md', specialty: 'technical leadership, architecture' }, 'Engineering Architect': { file: 'engineering-architect.md', specialty: 'system design, technical architecture' }, 'Engineering Developer': { file: 'engineering-developer.md', specialty: 'implementation, coding' }, 'Engineering Quality Assurance': { file: 'engineering-quality-assurance.md', specialty: 'testing, validation' }, 'Engineering Product Manager': { file: 'engineering-product-manager.md', specialty: 'product management, requirements' }, 'Engineering User Experience': { file: 'engineering-user-experience.md', specialty: 'UX design, user interface' }, // Marketing team 'Marketing Director': { file: 'marketing-director.md', specialty: 'marketing strategy, campaigns' }, 'Marketing Viral Growth': { file: 'marketing-viral-growth.md', specialty: 'viral marketing, growth hacking' }, 'Marketing Brand Identity': { file: 'marketing-brand-identity.md', specialty: 'branding, identity design' }, 'Marketing Content Hooks': { file: 'marketing-content-hooks.md', specialty: 'content strategy, engagement' }, 'Marketing Lead Capture': { file: 'marketing-lead-capture.md', specialty: 'lead generation, conversion' }, // Content team 'Content Team Manager': { file: 'content-team-manager.md', specialty: 'content management, strategy' }, 'Content Calendar Planner': { file: 'content-calendar-planner.md', specialty: 'content planning, scheduling' }, 'Content Playbook Writer': { file: 'content-playbook-writer.md', specialty: 'playbook creation, documentation' }, // Research team 'Research Team Manager': { file: 'research-team-manager.md', specialty: 'research coordination, analysis' }, 'Research Foundation Analyst': { file: 'research-foundation-analyst.md', specialty: 'foundation research, market analysis' }, 'Research Market Analyst': { file: 'research-market-analyst.md', specialty: 'market research, competitive analysis' }, // Service team 'Service Team Manager': { file: 'service-team-manager.md', specialty: 'customer service, success management' }, 'Service Success Manager': { file: 'service-success-manager.md', specialty: 'customer success, retention' }, 'Service Advocacy Manager': { file: 'service-advocacy-manager.md', specialty: 'customer advocacy, support' }, // Crypto team 'Crypto Team Manager': { file: 'crypto-team-manager.md', specialty: 'crypto strategy, blockchain' }, 'Crypto Token Analyst': { file: 'crypto-token-analyst.md', specialty: 'token analysis, crypto markets' }, 'Crypto Market Researcher': { file: 'crypto-market-researcher.md', specialty: 'crypto market research' } }; } async createTask(storyId, taskName, options = {}) { console.log(chalk.blue('\\nšŸ“‹ Creating new task...')); // Step 1: Get story and validate it exists const { story, missionId } = await this.getStoryWithMission(storyId); if (!story) { throw new Error(`Story not found: ${storyId}`); } // Step 2: Generate task template with AI suggestions const taskTemplate = await this.generateTaskTemplate(taskName, story, options); // Step 3: Human-AI collaboration on task design const task = await this.collaborativelyDesignTask(taskTemplate, options); // Step 4: Create task file and assign to story await this.initializeTask(missionId, storyId, task); console.log(chalk.green(`\\nāœ… Task "${task.name}" created successfully!`)); console.log(chalk.cyan(`šŸ“‹ Task ID: ${task.id}`)); console.log(chalk.cyan(`šŸ¤– Assigned agents: ${task.agents.join(', ')}`)); return task; } async generateTaskTemplate(taskName, story, options = {}) { console.log(chalk.gray('🧠 Generating task template...')); const template = { name: taskName, id: this.generateTaskId(taskName), storyId: story.id, description: `Task: ${taskName}`, suggestedAgents: this.suggestTaskAgents(taskName, story), estimatedDuration: this.estimateTaskDuration(taskName, story), priority: this.assessTaskPriority(taskName, story), dependencies: this.identifyTaskDependencies(taskName, story), acceptanceCriteria: this.generateAcceptanceCriteria(taskName, story), techContext: this.extractTechContext(taskName, story) }; return template; } generateTaskId(taskName) { const hash = this.hashString(taskName).substring(0, 8); return `task-${hash}`; } hashString(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash).toString(16); } suggestTaskAgents(taskName, story) { const agents = new Set(); const taskLower = taskName.toLowerCase(); const storyObjective = story.objective?.toLowerCase() || ''; // Always include Task Master for coordination agents.add('Task Master'); // Technical implementation tasks if (taskLower.includes('implement') || taskLower.includes('code') || taskLower.includes('develop')) { agents.add('Engineering Developer'); agents.add('Engineering Director'); } // Architecture and design tasks if (taskLower.includes('design') || taskLower.includes('architecture') || taskLower.includes('structure')) { agents.add('Engineering Architect'); if (taskLower.includes('ui') || taskLower.includes('user')) { agents.add('Engineering User Experience'); } } // Testing and quality tasks if (taskLower.includes('test') || taskLower.includes('quality') || taskLower.includes('validation')) { agents.add('Engineering Quality Assurance'); } // Marketing and content tasks if (taskLower.includes('marketing') || taskLower.includes('campaign') || taskLower.includes('growth')) { agents.add('Marketing Director'); if (taskLower.includes('viral') || taskLower.includes('growth')) { agents.add('Marketing Viral Growth'); } if (taskLower.includes('brand') || taskLower.includes('identity')) { agents.add('Marketing Brand Identity'); } } // Content creation tasks if (taskLower.includes('content') || taskLower.includes('write') || taskLower.includes('documentation')) { agents.add('Content Team Manager'); if (taskLower.includes('playbook') || taskLower.includes('guide')) { agents.add('Content Playbook Writer'); } } // Research tasks if (taskLower.includes('research') || taskLower.includes('analysis') || taskLower.includes('investigate')) { agents.add('Research Team Manager'); if (taskLower.includes('market') || taskLower.includes('competitive')) { agents.add('Research Market Analyst'); } } // Crypto tasks if (taskLower.includes('crypto') || taskLower.includes('token') || taskLower.includes('blockchain')) { agents.add('Crypto Team Manager'); if (taskLower.includes('token') || taskLower.includes('analysis')) { agents.add('Crypto Token Analyst'); } } // Service tasks if (taskLower.includes('service') || taskLower.includes('customer') || taskLower.includes('support')) { agents.add('Service Team Manager'); if (taskLower.includes('success') || taskLower.includes('retention')) { agents.add('Service Success Manager'); } } // Include story agents that are relevant const relevantStoryAgents = story.agents?.filter(agent => { const agentLower = agent.toLowerCase(); // Match agent specialty to task type if (taskLower.includes('implement') && agentLower.includes('developer')) return true; if (taskLower.includes('design') && agentLower.includes('architect')) return true; if (taskLower.includes('test') && agentLower.includes('quality')) return true; if (taskLower.includes('marketing') && agentLower.includes('marketing')) return true; return false; }) || []; relevantStoryAgents.forEach(agent => agents.add(agent)); // Ensure minimum agent assignment if (agents.size < 2) { agents.add('Engineering Director'); agents.add('Story Teller'); } return Array.from(agents); } estimateTaskDuration(taskName, story) { let baseHours = 4; // Default task duration const taskLower = taskName.toLowerCase(); const complexityKeywords = ['complex', 'integration', 'architecture', 'migration', 'refactor']; const simpleKeywords = ['fix', 'update', 'small', 'minor', 'simple', 'quick']; // Adjust based on task complexity if (complexityKeywords.some(keyword => taskLower.includes(keyword))) { baseHours *= 2; } else if (simpleKeywords.some(keyword => taskLower.includes(keyword))) { baseHours /= 2; } // Adjust based on task type if (taskLower.includes('research') || taskLower.includes('analysis')) { baseHours += 2; } if (taskLower.includes('implement') || taskLower.includes('develop')) { baseHours += 4; } baseHours = Math.max(1, baseHours); // Minimum 1 hour if (baseHours <= 4) { return `${baseHours} hours`; } else if (baseHours <= 8) { return `${Math.ceil(baseHours)} hours (1 day)`; } else { return `${Math.ceil(baseHours/8)} days`; } } assessTaskPriority(taskName, story) { const taskLower = taskName.toLowerCase(); // Inherit story priority as baseline let basePriority = story.priority || 'medium'; // High priority indicators const highPriorityKeywords = ['critical', 'urgent', 'blocker', 'security', 'bug', 'fix', 'error']; if (highPriorityKeywords.some(keyword => taskLower.includes(keyword))) { return 'high'; } // Low priority indicators const lowPriorityKeywords = ['nice-to-have', 'enhancement', 'optional', 'future', 'cleanup']; if (lowPriorityKeywords.some(keyword => taskLower.includes(keyword))) { return 'low'; } return basePriority; } identifyTaskDependencies(taskName, story) { const dependencies = []; const taskLower = taskName.toLowerCase(); // Common dependency patterns if (taskLower.includes('test') && !taskLower.includes('plan')) { dependencies.push('Implementation completed'); } if (taskLower.includes('deploy') || taskLower.includes('release')) { dependencies.push('Testing completed'); dependencies.push('Code review approved'); } if (taskLower.includes('integration')) { dependencies.push('Individual components completed'); } return dependencies; } generateAcceptanceCriteria(taskName, story) { const criteria = []; const taskLower = taskName.toLowerCase(); // Common acceptance criteria patterns if (taskLower.includes('implement') || taskLower.includes('develop')) { criteria.push('Code is implemented according to specifications'); criteria.push('Code passes all existing tests'); criteria.push('Code follows project coding standards'); criteria.push('Implementation is documented'); } if (taskLower.includes('test')) { criteria.push('Test cases cover all requirements'); criteria.push('All tests pass successfully'); criteria.push('Test coverage meets project standards'); } if (taskLower.includes('design')) { criteria.push('Design meets user requirements'); criteria.push('Design is reviewed and approved'); criteria.push('Design specifications are documented'); } if (taskLower.includes('research')) { criteria.push('Research findings are documented'); criteria.push('Recommendations are provided'); criteria.push('Results are peer-reviewed'); } // Default criteria if none match if (criteria.length === 0) { criteria.push('Task objectives are met'); criteria.push('Deliverables are completed'); criteria.push('Quality standards are maintained'); } return criteria; } extractTechContext(taskName, story) { // Get mission context to understand tech stack const context = { frameworks: [], languages: [], tools: [], patterns: [] }; const taskLower = taskName.toLowerCase(); // Common framework patterns if (taskLower.includes('react') || story.objective?.includes('React')) { context.frameworks.push('React'); context.languages.push('JavaScript'); } if (taskLower.includes('node') || taskLower.includes('express')) { context.frameworks.push('Node.js'); context.languages.push('JavaScript'); } if (taskLower.includes('api') || taskLower.includes('endpoint')) { context.patterns.push('REST API'); } if (taskLower.includes('database') || taskLower.includes('data')) { context.patterns.push('Database Integration'); } return context; } async collaborativelyDesignTask(template, options) { console.log(chalk.blue('\\nšŸ¤ Collaborative task design...')); // Show AI analysis console.log(chalk.cyan('\\nšŸ¤– AI Analysis:')); console.log(` Task: ${template.name}`); console.log(` Estimated duration: ${template.estimatedDuration}`); console.log(` Priority: ${template.priority}`); console.log(` Suggested agents: ${template.suggestedAgents.join(', ')}`); console.log(` Dependencies: ${template.dependencies.join(', ') || 'None'}`); if (options.interactive !== false) { // Get human input const answers = await inquirer.prompt([ { type: 'input', name: 'name', message: 'Task name:', default: template.name }, { type: 'editor', name: 'description', message: 'Task description:', default: `# ${template.name}\\n\\n[Describe what this task will accomplish and how it contributes to the story]` }, { type: 'checkbox', name: 'agents', message: 'Select agents for this task:', choices: template.suggestedAgents, default: template.suggestedAgents.slice(0, 3) }, { type: 'list', name: 'priority', message: 'Task priority:', choices: ['high', 'medium', 'low'], default: template.priority }, { type: 'input', name: 'duration', message: 'Expected duration:', default: template.estimatedDuration } ]); // Merge human input with AI suggestions Object.assign(template, answers); template.acceptanceCriteria = template.acceptanceCriteria; // Keep AI suggestions } else { // Non-interactive mode template.agents = template.suggestedAgents.slice(0, 3); template.duration = template.estimatedDuration; template.description = `# ${template.name}\\n\\nAI-generated task: ${template.name.toLowerCase()}.`; } console.log(chalk.green('āœ… Task design complete')); return template; } async initializeTask(missionId, storyId, task) { console.log(chalk.gray('šŸ“ Creating task structure...')); const storyDir = path.join(this.missionsDir, missionId, 'stories'); const tasksDir = path.join(storyDir, 'tasks'); // Ensure tasks directory exists await fs.ensureDir(tasksDir); // Create task data const taskData = { name: task.name, id: task.id, storyId: storyId, missionId: missionId, description: task.description, status: 'pending', priority: task.priority, created: new Date().toISOString(), agents: task.agents, duration: task.duration, dependencies: task.dependencies, acceptanceCriteria: task.acceptanceCriteria, techContext: task.techContext, progress: { started: null, completed: null, notes: [] } }; // Save task file await fs.writeFile( path.join(tasksDir, `${task.id}.yaml`), yaml.dump(taskData, { indent: 2 }) ); // Update story with new task await this.addTaskToStory(missionId, storyId, task.id, task.name); // Create task README const readmeContent = `# ${task.name} ${task.description || `Task: ${task.name}`} ## Acceptance Criteria ${task.acceptanceCriteria.map(criteria => `- [ ] ${criteria}`).join('\\n')} ## Agents ${task.agents.map(agent => `- ${agent}`).join('\\n')} ## Details - Priority: ${task.priority} - Duration: ${task.duration} - Status: ${taskData.status} - Created: ${taskData.created} ## Dependencies ${task.dependencies.length > 0 ? task.dependencies.map(dep => `- ${dep}`).join('\\n') : 'None'} ## Tech Context - Frameworks: ${task.techContext.frameworks.join(', ') || 'None'} - Languages: ${task.techContext.languages.join(', ') || 'None'} - Patterns: ${task.techContext.patterns.join(', ') || 'None'} ## Progress Notes _Task progress and notes will be tracked here_ `; await fs.writeFile(path.join(tasksDir, `${task.id}.md`), readmeContent); return taskData; } async addTaskToStory(missionId, storyId, taskId, taskName) { const storyPath = path.join(this.missionsDir, missionId, 'stories', `${storyId}.yaml`); if (await fs.pathExists(storyPath)) { const story = yaml.load(await fs.readFile(storyPath, 'utf8')); // Add task to story if not already there const taskExists = story.tasks.some(task => (typeof task === 'string' && task === taskName) || (typeof task === 'object' && (task.id === taskId || task.name === taskName)) ); if (!taskExists) { story.tasks.push({ name: taskName, id: taskId, status: 'pending', created: new Date().toISOString() }); await fs.writeFile(storyPath, yaml.dump(story, { indent: 2 })); } } } async listTasks(storyId = null, missionId = null) { const tasks = []; if (storyId && missionId) { // List tasks for specific story const tasksDir = path.join(this.missionsDir, missionId, 'stories', 'tasks'); if (await fs.pathExists(tasksDir)) { const taskFiles = await fs.readdir(tasksDir); for (const file of taskFiles) { if (file.endsWith('.yaml')) { const taskPath = path.join(tasksDir, file); const taskData = yaml.load(await fs.readFile(taskPath, 'utf8')); if (taskData.storyId === storyId) { tasks.push(taskData); } } } } } else { // List all tasks across all missions/stories if (await fs.pathExists(this.missionsDir)) { const missionDirs = await fs.readdir(this.missionsDir); for (const missionDir of missionDirs) { const storiesDir = path.join(this.missionsDir, missionDir, 'stories'); if (await fs.pathExists(storiesDir)) { const storyItems = await fs.readdir(storiesDir); for (const item of storyItems) { if (item === 'tasks') { const tasksDir = path.join(storiesDir, 'tasks'); const taskFiles = await fs.readdir(tasksDir); for (const file of taskFiles) { if (file.endsWith('.yaml')) { const taskPath = path.join(tasksDir, file); const taskData = yaml.load(await fs.readFile(taskPath, 'utf8')); tasks.push(taskData); } } } } } } } } return tasks.sort((a, b) => new Date(b.created) - new Date(a.created)); } async getTask(taskId, storyId = null, missionId = null) { if (storyId && missionId) { // Look in specific story const taskPath = path.join(this.missionsDir, missionId, 'stories', 'tasks', `${taskId}.yaml`); if (await fs.pathExists(taskPath)) { return yaml.load(await fs.readFile(taskPath, 'utf8')); } } else { // Search across all tasks const tasks = await this.listTasks(); return tasks.find(task => task.id === taskId); } return null; } async updateTaskStatus(taskId, status, notes = null) { const task = await this.getTask(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } task.status = status; task.updated = new Date().toISOString(); // Update progress tracking if (status === 'active' && !task.progress.started) { task.progress.started = new Date().toISOString(); } else if (status === 'completed') { task.progress.completed = new Date().toISOString(); } if (notes) { task.progress.notes.push({ timestamp: new Date().toISOString(), note: notes }); } // Find and update the task file const taskPath = await this.findTaskPath(taskId); if (taskPath && await fs.pathExists(taskPath)) { await fs.writeFile(taskPath, yaml.dump(task, { indent: 2 })); return task; } throw new Error(`Could not update task file: ${taskId}`); } async findTaskPath(taskId) { const tasks = await this.listTasks(); const task = tasks.find(t => t.id === taskId); if (task) { return path.join(this.missionsDir, task.missionId, 'stories', 'tasks', `${taskId}.yaml`); } return null; } async assignTaskToAgent(taskId, agentName) { const task = await this.getTask(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } if (!this.agents[agentName]) { throw new Error(`Agent not found: ${agentName}`); } // Add agent if not already assigned if (!task.agents.includes(agentName)) { task.agents.push(agentName); task.updated = new Date().toISOString(); task.progress.notes.push({ timestamp: new Date().toISOString(), note: `Agent assigned: ${agentName}` }); // Update task file const taskPath = await this.findTaskPath(taskId); if (taskPath) { await fs.writeFile(taskPath, yaml.dump(task, { indent: 2 })); } } return task; } async getStoryWithMission(storyId) { // Search across all missions to find the story and its mission if (await fs.pathExists(this.missionsDir)) { const missionDirs = await fs.readdir(this.missionsDir); for (const missionDir of missionDirs) { const storiesDir = path.join(this.missionsDir, missionDir, 'stories'); if (await fs.pathExists(storiesDir)) { const storyPath = path.join(storiesDir, `${storyId}.yaml`); if (await fs.pathExists(storyPath)) { const story = yaml.load(await fs.readFile(storyPath, 'utf8')); return { story, missionId: missionDir }; } } } } return { story: null, missionId: null }; } } // CLI interface functions export async function createTask(storyId, taskName, options = {}) { const taskSystem = new TaskSystem(); return await taskSystem.createTask(storyId, taskName, options); } export async function listTasks(storyId = null, missionId = null) { const taskSystem = new TaskSystem(); return await taskSystem.listTasks(storyId, missionId); } export async function getTask(taskId, storyId = null, missionId = null) { const taskSystem = new TaskSystem(); return await taskSystem.getTask(taskId, storyId, missionId); } export async function updateTaskStatus(taskId, status, notes = null) { const taskSystem = new TaskSystem(); return await taskSystem.updateTaskStatus(taskId, status, notes); } export async function assignTaskToAgent(taskId, agentName) { const taskSystem = new TaskSystem(); return await taskSystem.assignTaskToAgent(taskId, agentName); }