import { z } from 'zod';
import { apiClient } from '../lib/api-client.js';
import { logger } from '../lib/logger.js';
import { authManager } from '../lib/auth.js';
import { initiativeHandlers } from './initiatives.js';
import { projectHandlers } from './projects.js';
import { taskHandlers } from './tasks.js';
import { documentHandlers } from './documents.js';
const ExecutionMode = z.enum(['analyze', 'create']);
const TaskPlanSchema = z.object({
title: z.string(),
description: z.string().optional(),
priority: z.enum(['low', 'medium', 'high']).default('medium'),
status: z.enum(['todo', 'in_progress', 'done']).default('todo'),
assignee_id: z.string().optional(),
due_date: z.string().optional()
});
const InitiativePlanSchema = z.object({
name: z.string(),
objective: z.string(),
description: z.string().optional(),
priority: z.enum(['critical', 'high', 'medium', 'low']).default('medium'),
status: z.enum(['planning', 'active', 'on_hold', 'completed', 'cancelled']).default('planning'),
start_date: z.string().optional(),
target_date: z.string().optional(),
tasks: z.array(TaskPlanSchema).default([])
});
const ProjectPlanSchema = z.object({
project: z.object({
name: z.string(),
description: z.string().optional(),
status: z.enum(['active', 'completed', 'archived']).default('active')
}),
initiatives: z.array(InitiativePlanSchema).default([]),
documents: z.array(z.object({
title: z.string(),
content: z.string(),
document_type: z.enum(['requirement', 'design', 'technical', 'meeting_notes', 'other'])
})).optional()
});
const PromptToProjectArgsSchema = z.object({
prompt: z.string().describe('Natural language description of the project to create'),
execution_mode: ExecutionMode.describe('analyze: return structured requirements for agent to process, create: execute agent-provided plan'),
project_plan: ProjectPlanSchema.optional().describe('Required when execution_mode is "create" - the structured plan to execute')
});
function extractKeyRequirements(prompt: string) {
const requirements = {
mentions_timeline: /deadline|timeline|due|by|until|before|sprint|milestone/i.test(prompt),
mentions_team: /team|developer|designer|engineer|member|person|people/i.test(prompt),
mentions_technology: /react|node|python|java|database|api|frontend|backend|fullstack/i.test(prompt),
mentions_features: /feature|functionality|capability|requirement|user story/i.test(prompt),
mentions_priority: /urgent|critical|high priority|important|asap|immediately/i.test(prompt),
is_technical: /api|database|frontend|backend|integration|deployment|testing/i.test(prompt),
is_business: /customer|user|market|revenue|sales|marketing|business/i.test(prompt)
};
return requirements;
}
function generateProjectTemplate(prompt: string) {
const requirements = extractKeyRequirements(prompt);
const template = {
project: {
name: "[Project name based on the prompt]",
description: "[Comprehensive project description expanding on: " + prompt + "]",
status: "active" as const
},
initiatives: [
{
name: "[Core Feature/Initiative 1]",
objective: "[What this initiative aims to achieve]",
description: "[Detailed description]",
priority: requirements.mentions_priority ? "high" as const : "medium" as const,
status: "planning" as const,
tasks: [
{
title: "[Specific task 1]",
description: "[Task details]",
priority: "medium" as const,
status: "todo" as const
}
]
}
],
documents: requirements.is_technical ? [
{
title: "Technical Specification",
content: "[Technical documentation for the project]",
document_type: "technical" as const
},
{
title: "Project Requirements",
content: "[Detailed requirements document]",
document_type: "requirement" as const
}
] : []
};
return template;
}
function generateAnalysisGuidance(prompt: string) {
const requirements = extractKeyRequirements(prompt);
const guidance = {
instructions: "Please analyze the project request and create a comprehensive project plan. Fill out the template below with specific details based on the requirements.",
key_considerations: [
"Break down the project into 2-4 strategic initiatives",
"Each initiative should have 3-7 specific, actionable tasks",
"Consider dependencies between tasks",
"Set realistic priorities based on project goals",
requirements.mentions_timeline && "Include timeline considerations in task planning",
requirements.mentions_team && "Consider team allocation and skills needed",
requirements.mentions_technology && "Include technical implementation tasks",
requirements.is_business && "Include business/user-facing deliverables"
].filter(Boolean),
suggested_initiatives: requirements.is_technical ? [
"Core Infrastructure Setup",
"Feature Development",
"Testing & Quality Assurance",
"Documentation & Deployment"
] : [
"Planning & Requirements",
"Implementation",
"Review & Iteration"
],
questions_to_consider: [
"What are the main objectives of this project?",
"What are the critical success factors?",
"What could be the potential risks or blockers?",
requirements.mentions_timeline && "What are the key milestones and deadlines?",
requirements.mentions_team && "What skills and resources are needed?",
"How should the work be prioritized?"
].filter(Boolean)
};
return guidance;
}
export const promptToProjectTools = [
{
name: 'prompt_to_project',
description: 'Convert a natural language project description into structured project entities. In analyze mode, returns a template for the AI agent to fill. In create mode, executes the agent-provided plan.',
inputSchema: {
type: 'object' as const,
properties: PromptToProjectArgsSchema.shape,
required: ['prompt', 'execution_mode'] as const,
additionalProperties: false
},
handler: async (args: unknown) => {
try {
const validatedArgs = PromptToProjectArgsSchema.parse(args);
if (validatedArgs.execution_mode === 'analyze') {
logger.info('Analyzing project prompt for agent processing', { prompt: validatedArgs.prompt });
const template = generateProjectTemplate(validatedArgs.prompt);
const guidance = generateAnalysisGuidance(validatedArgs.prompt);
const requirements = extractKeyRequirements(validatedArgs.prompt);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
mode: 'analyze',
guidance,
template,
requirements,
original_prompt: validatedArgs.prompt,
next_step: "Fill out the template with specific details and call this tool again with execution_mode='create' and the filled project_plan"
}, null, 2)
}
]
};
} else if (validatedArgs.execution_mode === 'create') {
if (!validatedArgs.project_plan) {
throw new Error('project_plan is required when execution_mode is "create"');
}
logger.info('Creating project from agent-provided plan', {
projectName: validatedArgs.project_plan.project.name
});
// Get the authenticated user context
// For MCP keys, we need a valid UUID. Use a placeholder UUID
let userId = '00000000-0000-0000-0000-000000000000'; // Null UUID as placeholder
try {
const authContext = authManager.getAuthContext();
if (authContext.userId && authContext.userId !== 'mcp-pending') {
userId = authContext.userId;
} else {
logger.info('Using placeholder UUID for MCP context owner_id');
}
} catch (e) {
// If not authenticated yet, use placeholder
logger.info('Using placeholder UUID for MCP context owner_id');
}
// Use the MCP handlers directly to ensure proper authentication and association
// Create the project using the MCP handler
const projectResult = await projectHandlers.create_project({
name: validatedArgs.project_plan.project.name,
description: validatedArgs.project_plan.project.description,
status: validatedArgs.project_plan.project.status
});
const createdProject = projectResult.project;
const createdInitiatives = [];
const createdTasks = [];
const createdDocuments = [];
// Create initiatives and their tasks
for (const initiative of validatedArgs.project_plan.initiatives) {
try {
// Use the MCP handler which properly handles project association
const initiativeResult = await initiativeHandlers.create_initiative({
name: initiative.name,
objective: initiative.objective,
description: initiative.description,
priority: initiative.priority,
status: initiative.status,
owner_id: userId, // Required field
project_ids: [createdProject.id], // Associate with the created project
start_date: initiative.start_date,
target_date: initiative.target_date,
metadata: {},
tags: []
});
const createdInitiative = initiativeResult.initiative;
createdInitiatives.push(createdInitiative);
// Create tasks for this initiative
for (const task of initiative.tasks) {
try {
const taskResult = await taskHandlers.create_task({
project_id: createdProject.id,
initiative_id: createdInitiative.id,
title: task.title,
description: task.description,
priority: task.priority,
status: task.status,
assignee_id: task.assignee_id,
due_date: task.due_date
});
createdTasks.push(taskResult.task);
} catch (taskError) {
logger.error('Failed to create task', {
task: task.title,
error: taskError
});
}
}
} catch (initiativeError) {
logger.error('Failed to create initiative', {
initiative: initiative.name,
error: initiativeError
});
}
}
// Create documents if provided
if (validatedArgs.project_plan.documents) {
for (const doc of validatedArgs.project_plan.documents) {
try {
const docResult = await documentHandlers.create_document({
project_id: createdProject.id,
title: doc.title,
content: doc.content,
document_type: doc.document_type
});
createdDocuments.push(docResult.document);
} catch (docError) {
logger.error('Failed to create document', {
document: doc.title,
error: docError
});
}
}
}
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
mode: 'create',
success: true,
created: {
project: {
id: createdProject.id,
name: createdProject.name,
description: createdProject.description
},
initiatives: createdInitiatives.map(i => ({
id: i.id,
name: i.name,
objective: i.objective
})),
tasks: createdTasks.map(t => ({
id: t.id,
title: t.title,
initiative_id: t.initiative_id
})),
documents: createdDocuments.map(d => ({
id: d.id,
title: d.title,
type: d.document_type
}))
},
summary: `Successfully created project "${createdProject.name}" with ${createdInitiatives.length} initiatives, ${createdTasks.length} tasks, and ${createdDocuments.length} documents.`
}, null, 2)
}
]
};
} else {
throw new Error(`Invalid execution_mode: ${validatedArgs.execution_mode}`);
}
} catch (error) {
logger.error('Error in prompt_to_project tool', { error });
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
error: errorMessage,
details: error instanceof z.ZodError ? error.errors : undefined
}, null, 2)
}
],
isError: true
};
}
}
}
];