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
JavaScript
/**
* 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);
}