Skip to main content
Glama

overseer.update_phases

Modify phase definitions in project workflows by renaming, updating descriptions, or adjusting steps, deliverables, and completion criteria.

Instructions

Updates existing phase definitions (rename, modify description, add/remove steps, deliverables, done criteria).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repo_rootYesRoot path of the repository
modificationsYesList of modifications to apply

Implementation Reference

  • The main handler function that executes the tool logic: reads existing phases from PHASES.md, applies modifications (add, update, remove), and writes updated phases back to the file.
    export async function handleUpdatePhases(
      args: {
        repo_root: string;
        modifications: Array<{
          operation: 'add' | 'update' | 'remove';
          phase_id?: string;
          phase?: {
            id: string;
            name: string;
            description: string;
            deliverables?: string[];
            done_criteria?: string[];
          };
        }>;
      },
      phaseManager: PhaseManager
    ): Promise<{
      success: boolean;
      message: string;
      changes_applied: {
        added: string[];
        updated: string[];
        removed: string[];
      };
      files_written: string[];
      errors: string[];
    }> {
      const errors: string[] = [];
      const added: string[] = [];
      const updated: string[] = [];
      const removed: string[] = [];
      const filesWritten: string[] = [];
    
      try {
        // Resolve repo path
        let repoPath = args.repo_root;
        if (!repoPath.startsWith('/')) {
          repoPath = join(homedir(), 'dev', repoPath);
        }
        repoPath = FSUtils.expandPath(repoPath);
    
        // Extract repo name from path
        const repoName = repoPath.split('/').pop() || 'unknown';
    
        // Read existing phases
        // We need to create a RepoHandler that can read from the absolute path
        // For now, let's read directly and parse
        const phasesPath = join(repoPath, 'PHASES.md');
        if (!FSUtils.fileExists(phasesPath)) {
          return {
            success: false,
            message: 'Project not found. Run plan_project first.',
            changes_applied: { added: [], updated: [], removed: [] },
            files_written: [],
            errors: ['PHASES.md not found'],
          };
        }
    
        // Use RepoHandler with absolute path (handles paths with spaces)
        const repoHandler = new RepoHandler();
        let projectPhases = repoHandler.readPhasesIndexFromPath(repoPath);
        
        if (!projectPhases) {
          return {
            success: false,
            message: 'Project not found. Run plan_project first.',
            changes_applied: { added: [], updated: [], removed: [] },
            files_written: [],
            errors: ['PHASES.md not found'],
          };
        }
    
        // Apply modifications
        for (const mod of args.modifications) {
          try {
            if (mod.operation === 'add') {
              if (!mod.phase) {
                errors.push('Phase data required for add operation');
                continue;
              }
    
              // Validate required fields
              if (!mod.phase.id || !mod.phase.name || !mod.phase.description) {
                errors.push('Phase must have id, name, and description');
                continue;
              }
    
              // Check if phase ID already exists
              if (projectPhases.phases.some(p => p.id === mod.phase!.id)) {
                errors.push(`Phase ID ${mod.phase.id} already exists`);
                continue;
              }
    
              // Add phase
              const newPhase: PhaseInfo = {
                id: mod.phase.id,
                name: mod.phase.name,
                status: 'pending',
                description: mod.phase.description,
                deliverables: mod.phase.deliverables,
                done_criteria: mod.phase.done_criteria,
              };
    
              projectPhases.phases.push(newPhase);
              added.push(mod.phase.id);
    
            } else if (mod.operation === 'update') {
              if (!mod.phase_id) {
                errors.push('phase_id required for update operation');
                continue;
              }
    
              const phaseIndex = projectPhases.phases.findIndex(p => p.id === mod.phase_id);
              if (phaseIndex === -1) {
                errors.push(`Phase ID ${mod.phase_id} not found`);
                continue;
              }
    
              if (!mod.phase) {
                errors.push('Phase data required for update operation');
                continue;
              }
    
              // Update phase
              const existingPhase = projectPhases.phases[phaseIndex];
              projectPhases.phases[phaseIndex] = {
                ...existingPhase,
                name: mod.phase.name || existingPhase.name,
                description: mod.phase.description || existingPhase.description,
                deliverables: mod.phase.deliverables !== undefined ? mod.phase.deliverables : existingPhase.deliverables,
                done_criteria: mod.phase.done_criteria !== undefined ? mod.phase.done_criteria : existingPhase.done_criteria,
              };
    
              updated.push(mod.phase_id);
    
            } else if (mod.operation === 'remove') {
              if (!mod.phase_id) {
                errors.push('phase_id required for remove operation');
                continue;
              }
    
              const phaseIndex = projectPhases.phases.findIndex(p => p.id === mod.phase_id);
              if (phaseIndex === -1) {
                errors.push(`Phase ID ${mod.phase_id} not found`);
                continue;
              }
    
              projectPhases.phases.splice(phaseIndex, 1);
              removed.push(mod.phase_id);
            }
          } catch (error) {
            const errorMsg = error instanceof Error ? error.message : String(error);
            errors.push(`Failed to apply modification: ${errorMsg}`);
          }
        }
    
        // Write updated PHASES.md
        if (added.length > 0 || updated.length > 0 || removed.length > 0) {
          try {
            repoHandler.writePhasesIndexToPath(repoPath, projectPhases);
            filesWritten.push(join(repoPath, 'PHASES.md'));
          } catch (error) {
            errors.push(`Failed to write PHASES.md: ${error}`);
          }
        }
    
        const success = errors.length === 0;
        const message = success
          ? `Applied ${added.length} additions, ${updated.length} updates, ${removed.length} removals`
          : `Partially applied changes with ${errors.length} error(s)`;
    
        return {
          success,
          message,
          changes_applied: {
            added,
            updated,
            removed,
          },
          files_written: filesWritten,
          errors,
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          success: false,
          message: `Failed to update phases: ${errorMessage}`,
          changes_applied: { added: [], updated: [], removed: [] },
          files_written: [],
          errors: [errorMessage],
        };
      }
    }
  • Defines the tool schema including name, description, and detailed inputSchema for modifications.
    export function createUpdatePhasesTool(phaseManager: PhaseManager): Tool {
      return {
        name: 'overseer.update_phases',
        description: 'Updates existing phase definitions (rename, modify description, add/remove steps, deliverables, done criteria).',
        inputSchema: {
          type: 'object',
          required: ['repo_root', 'modifications'],
          properties: {
            repo_root: {
              type: 'string',
              description: 'Root path of the repository',
            },
            modifications: {
              type: 'array',
              items: {
                type: 'object',
                properties: {
                  operation: {
                    type: 'string',
                    enum: ['add', 'update', 'remove'],
                    description: 'Operation to perform',
                  },
                  phase_id: {
                    type: 'string',
                    description: 'Phase ID to update or remove (required for update/remove)',
                  },
                  phase: {
                    type: 'object',
                    description: 'Phase data (required for add/update)',
                    properties: {
                      id: { type: 'string' },
                      name: { type: 'string' },
                      description: { type: 'string' },
                      deliverables: {
                        type: 'array',
                        items: { type: 'string' },
                      },
                      done_criteria: {
                        type: 'array',
                        items: { type: 'string' },
                      },
                    },
                  },
                },
                required: ['operation'],
              },
              description: 'List of modifications to apply',
            },
          },
        },
      };
    }
  • Tool handler registration in the central switch dispatcher in handleToolCall.
    case 'overseer.update_phases':
      return await handleUpdatePhases(args, context.phaseManager);
  • Tool creation and registration in the createTools array.
    createUpdatePhasesTool(context.phaseManager),
  • src/tools/index.ts:9-9 (registration)
    Import of the tool creator and handler from update-phases.ts.
    import { createUpdatePhasesTool, handleUpdatePhases } from './update-phases.js';

Latest Blog Posts

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/freqkflag/PROJECT-OVERSEER-MCP'

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