Skip to main content
Glama

IT-MCP

by acampkin95
taskDecomposition.ts14.9 kB
import type { ThoughtRecord } from "./structuredThinking.js"; export type TaskStatus = "pending" | "in_progress" | "completed" | "blocked" | "on_hold"; export interface TaskNode { readonly id: string; readonly title: string; readonly description?: string; readonly status?: TaskStatus; readonly parallelGroup?: string; readonly dependencies?: readonly string[]; readonly notes?: readonly string[]; readonly tags?: readonly string[]; readonly owners?: readonly string[]; readonly estimateHours?: number; readonly externalRefs?: readonly { system: "linear" | "notion" | string; id: string }[]; } export interface DevOpsTask extends TaskNode { readonly category: "build" | "test" | "deploy" | "debug" | "schema" | "report" | "ops"; readonly checklist?: readonly string[]; readonly artifacts?: readonly string[]; readonly ciStage?: string; } export interface DebugTrack { readonly id: string; readonly title: string; readonly hypothesis?: string; readonly tasks: DevOpsTask[]; readonly status: TaskStatus; } export interface CiStage { readonly id: string; readonly name: string; readonly description?: string; readonly tasks: DevOpsTask[]; } export interface TaskDecompositionResult { readonly rootTask: string; readonly tasks: DevOpsTask[]; readonly parallelTracks: Record<string, DevOpsTask[]>; readonly criticalPath: string[]; readonly ciPipeline: CiStage[]; readonly debugTracks: DebugTrack[]; readonly summary: string; } export interface TaskDecompositionInput { readonly goal: string; readonly context?: string; readonly assumptions?: readonly string[]; readonly constraints?: readonly string[]; readonly stages?: readonly string[]; readonly thoughts?: ThoughtRecord[]; readonly maxDepth?: number; } export class TaskDecompositionService { public constructor(private readonly defaultOwner: string | undefined = undefined) {} public buildDecomposition(input: TaskDecompositionInput): TaskDecompositionResult { const stages = input.stages && input.stages.length > 0 ? input.stages : [ "Understand requirements", "Design solution", "Implement", "Validate", ]; const tasks: DevOpsTask[] = []; const parallelTracks: Record<string, DevOpsTask[]> = {}; const criticalPath: string[] = []; const debugTracks: DebugTrack[] = []; stages.forEach((stage, index) => { const id = `S${index + 1}`; const groupId = `phase-${index + 1}`; const stageTask: DevOpsTask = { id, title: stage, description: this.describeStage(stage, input), parallelGroup: groupId, dependencies: index === 0 ? [] : [`S${index}`], category: this.inferCategoryFromStage(stage), }; tasks.push(stageTask); criticalPath.push(id); if (!parallelTracks[groupId]) { parallelTracks[groupId] = []; } const parallelSubtasks = this.parallelActivitiesForStage(stage, input); parallelSubtasks.forEach((task, subIndex) => { const childId = `${id}.${subIndex + 1}`; const childTask: DevOpsTask = { id: childId, title: task.title, description: task.description, parallelGroup: stageTask.parallelGroup, dependencies: [id, ...(task.dependencies ?? [])], notes: task.notes, checklist: task.checklist, artifacts: task.artifacts, category: task.category ?? stageTask.category, ciStage: task.ciStage ?? stageTask.parallelGroup, }; tasks.push(childTask); parallelTracks[groupId].push(childTask); }); }); if (input.thoughts?.length) { const enriched = this.enrichTasksFromThoughts(tasks, input.thoughts); enriched.debugTracks.forEach((track) => debugTracks.push(track)); } const ciPipeline = this.buildCiPipeline(tasks); const summaryLines = [ `Goal: ${input.goal}`, stages.map((stage, index) => `Phase ${index + 1}: ${stage}`).join(" -> "), ]; if (input.assumptions?.length) { summaryLines.push(`Assumptions: ${input.assumptions.join(", ")}`); } if (input.constraints?.length) { summaryLines.push(`Constraints: ${input.constraints.join(", ")}`); } return { rootTask: input.goal, tasks, parallelTracks, criticalPath, ciPipeline, debugTracks, summary: summaryLines.join("\n"), }; } private describeStage(stage: string, input: TaskDecompositionInput): string { const base = `Stage: ${stage}`; if (input.context) { return `${base}\nContext: ${input.context}`; } return base; } private parallelActivitiesForStage( stage: string, input: TaskDecompositionInput, ): Array<{ title: string; description?: string; dependencies?: string[]; notes?: string[]; checklist?: string[]; artifacts?: string[]; category?: DevOpsTask["category"]; ciStage?: string; }> { const activities: Array<{ title: string; description?: string; dependencies?: string[]; notes?: string[]; checklist?: string[]; artifacts?: string[]; category?: DevOpsTask["category"]; ciStage?: string; }> = []; const lower = stage.toLowerCase(); if (lower.includes("understand")) { activities.push( { title: "Clarify success criteria", description: "Interview stakeholders or review project brief to nail down success metrics.", category: "report", checklist: ["Collect success metrics", "Document owner expectations"], }, { title: "Identify risks", description: "List potential blockers and unknowns to track during execution.", category: "debug", checklist: ["Threat model critical flows", "Add mitigation plan"], }, ); } else if (lower.includes("design")) { activities.push( { title: "Draft architecture options", description: "Sketch at least two viable approaches and compare trade-offs.", category: "build", artifacts: ["architecture-options.md"], }, { title: "Review with peers", description: "Run an asynchronous review or live walkthrough to validate the approach.", category: "report", checklist: ["Collect feedback", "Document decisions"], }, ); } else if (lower.includes("implement")) { activities.push( { title: "Parallel workstreams", description: "Assign separate owners to independent subcomponents to maximise throughput.", category: "build", ciStage: "build", }, { title: "Set up observability", description: "Ensure logging/metrics are in place to support debugging during this phase.", category: "ops", checklist: ["Provision dashboards", "Define alert thresholds"], }, ); } else if (lower.includes("validate")) { activities.push( { title: "Regression testing", description: "Run automated suites plus targeted exploratory testing.", category: "test", ciStage: "test", }, { title: "Rollback plan", description: "Document contingency steps in case issues surface post-deploy.", category: "deploy", checklist: ["Write rollback runbook", "Test recovery scripts"], }, ); } if (input.assumptions?.length) { activities.push({ title: "Validate assumptions", description: `Confirm assumptions hold: ${input.assumptions.join(", ")}`, category: "report", checklist: ["Check assumption status"], }); } return activities; } private inferCategoryFromStage(stage: string): DevOpsTask["category"] { const lower = stage.toLowerCase(); if (lower.includes("design")) { return "build"; } if (lower.includes("implement")) { return "build"; } if (lower.includes("validate")) { return "test"; } if (lower.includes("deploy")) { return "deploy"; } if (lower.includes("debug")) { return "debug"; } return "ops"; } private enrichTasksFromThoughts( tasks: DevOpsTask[], thoughts: ThoughtRecord[], ): { debugTracks: DebugTrack[] } { const debugTracks: DebugTrack[] = []; const taskMap = new Map<string, DevOpsTask>(tasks.map((task) => [task.id, task])); const stageBuckets = new Map<string, DevOpsTask[]>(); tasks.forEach((task) => { if (!stageBuckets.has(task.parallelGroup ?? "root")) { stageBuckets.set(task.parallelGroup ?? "root", []); } stageBuckets.get(task.parallelGroup ?? "root")?.push(task); }); const branchGroups = new Map<string, ThoughtRecord[]>(); for (const thought of thoughts) { if (thought.metadata?.branchId) { const branch = thought.metadata.branchId; if (!branchGroups.has(branch)) { branchGroups.set(branch, []); } branchGroups.get(branch)?.push(thought); } } for (const [branchId, branchThoughts] of branchGroups.entries()) { const trackTasks: DevOpsTask[] = branchThoughts.map((thought, index) => ({ id: `${branchId}-${index + 1}`, title: thought.thought, description: thought.metadata?.references?.join("\n"), category: this.mapTagsToCategory(thought.metadata?.tags ?? []) ?? "debug", checklist: thought.metadata?.tags, notes: thought.metadata?.references, parallelGroup: thought.stage, status: thought.metadata?.nextThoughtNeeded ? "in_progress" : "completed", ciStage: this.mapTagsToCiStage(thought.metadata?.tags ?? []) ?? "debug", })); debugTracks.push({ id: branchId, title: branchThoughts[0].stage, hypothesis: branchThoughts[0].metadata?.references?.join(", "), tasks: trackTasks, status: trackTasks.every((task) => task.status === "completed") ? "completed" : "in_progress", }); } thoughts.forEach((thought) => { const category = this.mapTagsToCategory(thought.metadata?.tags ?? []); const ciStage = this.mapTagsToCiStage(thought.metadata?.tags ?? []); const stageBucket = stageBuckets.get(`phase-${thought.metadata?.thoughtNumber ?? 0}`); const fallbackTask = stageBucket?.[0]; const targetTask = fallbackTask ? taskMap.get(fallbackTask.id) : undefined; if (targetTask) { const notes = [...(targetTask.notes ?? []), thought.thought]; const tags = [...(targetTask.tags ?? []), ...(thought.metadata?.tags ?? [])]; const checklist = [...(targetTask.checklist ?? []), ...(thought.metadata?.references ?? [])]; Object.assign(targetTask, { notes, tags, checklist, category: category ?? targetTask.category ?? "ops", ciStage: ciStage ?? targetTask.ciStage, }); } else { const fallbackStage = thought.stage; const syntheticTask: DevOpsTask = { id: `T-${taskMap.size + 1}`, title: thought.thought, description: thought.metadata?.references?.join("\n"), category: category ?? "ops", checklist: thought.metadata?.references, notes: thought.metadata?.tags, parallelGroup: fallbackStage, status: thought.metadata?.nextThoughtNeeded ? "in_progress" : "completed", ciStage: ciStage ?? fallbackStage, }; tasks.push(syntheticTask); taskMap.set(syntheticTask.id, syntheticTask); } }); return { debugTracks }; } private mapTagsToCategory(tags: readonly string[]): DevOpsTask["category"] | undefined { const lowerTags = tags.map((tag) => tag.toLowerCase()); if (lowerTags.some((tag) => tag.includes("deploy") || tag.includes("pipeline"))) { return "deploy"; } if (lowerTags.some((tag) => tag.includes("test") || tag.includes("qa"))) { return "test"; } if (lowerTags.some((tag) => tag.includes("schema") || tag.includes("database"))) { return "schema"; } if (lowerTags.some((tag) => tag.includes("debug") || tag.includes("incident"))) { return "debug"; } if (lowerTags.some((tag) => tag.includes("report") || tag.includes("status"))) { return "report"; } if (lowerTags.some((tag) => tag.includes("build") || tag.includes("compile"))) { return "build"; } if (lowerTags.some((tag) => tag.includes("ops") || tag.includes("infra"))) { return "ops"; } return undefined; } private mapTagsToCiStage(tags: readonly string[]): string | undefined { const lowerTags = tags.map((tag) => tag.toLowerCase()); if (lowerTags.some((tag) => tag.includes("build"))) { return "build"; } if (lowerTags.some((tag) => tag.includes("test"))) { return "test"; } if (lowerTags.some((tag) => tag.includes("deploy"))) { return "deploy"; } return undefined; } private buildCiPipeline(tasks: DevOpsTask[]): CiStage[] { const stages = new Map<string, CiStage>(); const stageOrder = ["build", "test", "deploy", "ops", "report"]; tasks.forEach((task) => { const stageName = task.ciStage ?? task.parallelGroup ?? task.category; if (!stageName) { return; } if (!stages.has(stageName)) { stages.set(stageName, { id: stageName, name: stageName, tasks: [], }); } stages.get(stageName)?.tasks.push(task); }); const sorted = Array.from(stages.values()).sort((a, b) => { const aIndex = stageOrder.indexOf(a.id); const bIndex = stageOrder.indexOf(b.id); if (aIndex === -1 && bIndex === -1) { return a.id.localeCompare(b.id); } if (aIndex === -1) { return 1; } if (bIndex === -1) { return -1; } return aIndex - bIndex; }); return sorted.map((stage) => ({ ...stage, description: `Tasks for ${stage.name}`, })); } public prepareLinearPayload(tasks: DevOpsTask[]): unknown { return { nodes: tasks.map((task) => ({ title: task.title, description: task.description, status: task.status ?? "pending", labels: task.tags ?? [], estimate: task.estimateHours, owner: task.owners?.[0] ?? this.defaultOwner, })), }; } public prepareNotionPayload(tasks: DevOpsTask[]): unknown { return tasks.map((task) => ({ Name: task.title, Status: task.status ?? "pending", Tags: task.tags ?? [], Notes: task.notes ?? [], })); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/acampkin95/MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server