Skip to main content
Glama

overseer.run_phase

Execute project phases by reading task files, checking completion status, and generating TODO items for incomplete tasks to maintain structured workflow progress.

Instructions

Execute a specific phase of a project. Reads tasks from PHASE-XX.md, checks completion status, and creates TODOs/stubs for incomplete tasks.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repo_rootYesRoot path of the repository
phase_idYesPhase ID (e.g., "01", "02")
aggression_levelNoHow aggressively to create files and make changesnormal

Implementation Reference

  • Main execution handler for overseer.run_phase tool. Processes PHASE-XX.md, checks/completes tasks, creates stubs/TODOs based on aggression_level, updates status and checklists.
    export async function handleRunPhase(
      args: {
        repo_root: string;
        phase_id: string;
        aggression_level?: 'bossmode' | 'normal' | 'conservative';
      },
      phaseManager: PhaseManager
    ): Promise<{
      success: boolean;
      message: string;
      phase_id: string;
      completed_tasks: Array<{
        task: string;
        type: 'deliverable' | 'done_criterion';
        evidence: string;
      }>;
      pending_tasks: Array<{
        task: string;
        type: 'deliverable' | 'done_criterion';
        action_taken: string;
      }>;
      changed_files: string[];
      status: 'in_progress' | 'potentially_complete';
    }> {
      const completedTasks: Array<{ task: string; type: 'deliverable' | 'done_criterion'; evidence: string }> = [];
      const pendingTasks: Array<{ task: string; type: 'deliverable' | 'done_criterion'; action_taken: string }> = [];
      const changedFiles: string[] = [];
    
      try {
        // Resolve repo path
        let repoPath = args.repo_root;
        if (!repoPath.startsWith('/')) {
          repoPath = join(homedir(), 'dev', repoPath);
        }
        repoPath = FSUtils.expandPath(repoPath);
    
        const phaseId = args.phase_id.padStart(2, '0');
        const aggressionLevel = args.aggression_level || 'normal';
    
        // Read PHASES.md using absolute path (handles paths with spaces)
        const repoName = repoPath.split('/').pop() || 'unknown';
        const repoHandler = new RepoHandler();
        const projectPhases = repoHandler.readPhasesIndexFromPath(repoPath);
    
        if (!projectPhases) {
          return {
            success: false,
            message: 'Project not found. Run plan_project first.',
            phase_id: phaseId,
            completed_tasks: [],
            pending_tasks: [],
            changed_files: [],
            status: 'in_progress',
          };
        }
    
        // Find the phase
        const phase = projectPhases.phases.find(p => p.id === phaseId);
        if (!phase) {
          return {
            success: false,
            message: `Phase ${phaseId} not found`,
            phase_id: phaseId,
            completed_tasks: [],
            pending_tasks: [],
            changed_files: [],
            status: 'in_progress',
          };
        }
    
        // Read PHASE-XX.md (handles paths with spaces)
        const phaseFilePath = repoHandler.getPhaseFileByIdFromPath(repoPath, phaseId);
        if (!FSUtils.fileExists(phaseFilePath)) {
          return {
            success: false,
            message: `Phase file PHASE-${phaseId}.md not found`,
            phase_id: phaseId,
            completed_tasks: [],
            pending_tasks: [],
            changed_files: [],
            status: 'in_progress',
          };
        }
    
        let phaseContent = FSUtils.readFile(phaseFilePath);
    
        // Parse deliverables and done_criteria from phase file
        const deliverables: string[] = [];
        const doneCriteria: string[] = [];
    
        // Extract deliverables
        const deliverablesMatch = phaseContent.match(/## Deliverables\s*\n((?:- \[[ x]\] .+\n?)+)/);
        if (deliverablesMatch) {
          const items = deliverablesMatch[1].match(/- \[[ x]\] (.+)/g);
          if (items) {
            items.forEach(item => {
              const text = item.replace(/- \[[ x]\] /, '').trim();
              if (text) deliverables.push(text);
            });
          }
        }
    
        // Extract done criteria
        const doneCriteriaMatch = phaseContent.match(/## Done Criteria\s*\n((?:- \[[ x]\] .+\n?)+)/);
        if (doneCriteriaMatch) {
          const items = doneCriteriaMatch[1].match(/- \[[ x]\] (.+)/g);
          if (items) {
            items.forEach(item => {
              const text = item.replace(/- \[[ x]\] /, '').trim();
              if (text) doneCriteria.push(text);
            });
          }
        }
    
        // Check each deliverable
        for (const deliverable of deliverables) {
          const checked = checkTaskCompletion(deliverable, repoPath, aggressionLevel);
          
          if (checked.completed) {
            completedTasks.push({
              task: deliverable,
              type: 'deliverable',
              evidence: checked.evidence,
            });
            // Update checklist in phase file
            phaseContent = updateChecklistItem(phaseContent, deliverable, true, 'Deliverables');
          } else {
            pendingTasks.push({
              task: deliverable,
              type: 'deliverable',
              action_taken: checked.actionTaken,
            });
            // Create stub/TODO if aggression level allows
            if (aggressionLevel !== 'conservative' && checked.filePath) {
              createStubFile(checked.filePath, deliverable, aggressionLevel);
              changedFiles.push(checked.filePath);
            }
            // Update checklist in phase file
            phaseContent = updateChecklistItem(phaseContent, deliverable, false, 'Deliverables');
          }
        }
    
        // Check each done criterion
        for (const criterion of doneCriteria) {
          const checked = checkTaskCompletion(criterion, repoPath, aggressionLevel);
          
          if (checked.completed) {
            completedTasks.push({
              task: criterion,
              type: 'done_criterion',
              evidence: checked.evidence,
            });
            phaseContent = updateChecklistItem(phaseContent, criterion, true, 'Done Criteria');
          } else {
            pendingTasks.push({
              task: criterion,
              type: 'done_criterion',
              action_taken: checked.actionTaken,
            });
            phaseContent = updateChecklistItem(phaseContent, criterion, false, 'Done Criteria');
          }
        }
    
        // Update phase status to in_progress if not already
        if (phase.status === 'pending') {
          phase.status = 'in_progress';
          phase.started_at = new Date().toISOString();
          repoHandler.writePhasesIndexToPath(repoPath, projectPhases);
          changedFiles.push(join(repoPath, 'PHASES.md'));
        }
    
        // Update phase file
        phaseContent = updatePhaseStatus(phaseContent, 'in_progress');
        FSUtils.writeFile(phaseFilePath, phaseContent);
        changedFiles.push(phaseFilePath);
    
        // Determine overall status
        const allComplete = completedTasks.length === deliverables.length + doneCriteria.length;
        const status = allComplete ? 'potentially_complete' : 'in_progress';
    
        return {
          success: true,
          message: `Phase ${phaseId} execution completed. ${completedTasks.length} tasks complete, ${pendingTasks.length} pending.`,
          phase_id: phaseId,
          completed_tasks: completedTasks,
          pending_tasks: pendingTasks,
          changed_files: changedFiles,
          status,
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          success: false,
          message: `Failed to run phase: ${errorMessage}`,
          phase_id: args.phase_id,
          completed_tasks: [],
          pending_tasks: [],
          changed_files: [],
          status: 'in_progress',
        };
      }
    }
  • Tool schema definition including name, description, and input schema parameters for overseer.run_phase.
    export function createRunPhaseTool(phaseManager: PhaseManager): Tool {
      return {
        name: 'overseer.run_phase',
        description: 'Execute a specific phase of a project. Reads tasks from PHASE-XX.md, checks completion status, and creates TODOs/stubs for incomplete tasks.',
        inputSchema: {
          type: 'object',
          required: ['repo_root', 'phase_id'],
          properties: {
            repo_root: {
              type: 'string',
              description: 'Root path of the repository',
            },
            phase_id: {
              type: 'string',
              description: 'Phase ID (e.g., "01", "02")',
            },
            aggression_level: {
              type: 'string',
              enum: ['bossmode', 'normal', 'conservative'],
              default: 'normal',
              description: 'How aggressively to create files and make changes',
            },
          },
        },
      };
    }
  • Registration of overseer.run_phase tool via createRunPhaseTool in the tools array returned by createTools.
    export function createTools(context: ToolContext): Tool[] {
      return [
        // Planning tools
        createPlanProjectTool(context.phaseManager),
        createInferPhasesTool(context.configLoader),
        createUpdatePhasesTool(context.phaseManager),
        // Execution tools
        createRunPhaseTool(context.phaseManager),
        createAdvancePhaseTool(context.phaseManager),
        createStatusTool(context.phaseManager),
        // QA tools
        createLintRepoTool(context.configLoader),
        createSyncDocsTool(context.phaseManager),
        createCheckComplianceTool(context.phaseManager),
        // Environment tools
        createEnvMapTool(context.phaseManager),
        createGenerateCiTool(context.phaseManager),
        createSecretsTemplateTool(context.phaseManager),
      ];
    }
  • Dispatcher in handleToolCall that routes 'overseer.run_phase' calls to the handleRunPhase function.
    export async function handleToolCall(
      name: string,
      args: any,
      context: ToolContext
    ): Promise<any> {
      switch (name) {
        // Planning tools
        case 'overseer.plan_project':
          return await handlePlanProject(args, context.phaseManager);
        case 'overseer.infer_phases':
          return await handleInferPhases(args, context.configLoader);
        case 'overseer.update_phases':
          return await handleUpdatePhases(args, context.phaseManager);
        // Execution tools
        case 'overseer.run_phase':
          return await handleRunPhase(args, context.phaseManager);
        case 'overseer.advance_phase':
          return await handleAdvancePhase(args, context.phaseManager);
        case 'overseer.status':
          return await handleStatus(args, context.phaseManager);
        // QA tools
        case 'overseer.lint_repo':
          return await handleLintRepo(args, context.configLoader);
        case 'overseer.sync_docs':
          return await handleSyncDocs(args, context.phaseManager);
        case 'overseer.check_compliance':
          return await handleCheckCompliance(args, context.phaseManager);
        // Environment tools
        case 'overseer.env_map':
          return await handleEnvMap(args, context.phaseManager);
        case 'overseer.generate_ci':
          return await handleGenerateCi(args, context.phaseManager);
        case 'overseer.secrets_template':
          return await handleSecretsTemplate(args, context.phaseManager);
        default:
          throw new Error(`Unknown tool: ${name}`);
      }
    }
  • Supporting helper functions for task checking, file stubbing, checklist updates, etc.
    }
    
    function checkTaskCompletion(

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