Skip to main content
Glama

overseer.plan_project

Plan software projects by creating phase definitions and documentation files. Generates PHASES.md and phase-specific files, optionally inferring structure from existing repositories.

Instructions

Plan a new project by creating phase definitions. Creates PHASES.md and PHASE-*.md files in the repository. Can infer phases from project structure if not provided.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repo_rootYesRoot path of the repository (absolute path or relative to ~/dev)
project_nameYesName of the project
project_summaryNoSummary description of the project
overwrite_existingNoIf true, overwrite existing PHASES.md. If false, normalize and merge.
phasesNoOptional: Explicit phase definitions. If not provided, phases will be inferred.

Implementation Reference

  • The main execution handler for the 'overseer.plan_project' tool. It resolves the repository path, infers or uses provided phases, generates and writes PHASES.md and individual PHASE-*.md files, handling overwrites and errors.
    export async function handlePlanProject(
      args: {
        repo_root: string;
        project_name: string;
        project_summary?: string;
        overwrite_existing?: boolean;
        phases?: Array<{
          id: string;
          name: string;
          description: string;
          deliverables?: string[];
          done_criteria?: string[];
        }>;
      },
      phaseManager: PhaseManager
    ): Promise<{
      success: boolean;
      message: string;
      phases_discovered: number;
      phases_created: number;
      files_written: string[];
      files_updated: string[];
      errors: string[];
    }> {
      const errors: string[] = [];
      const filesWritten: string[] = [];
      const filesUpdated: string[] = [];
    
      try {
        // Resolve repo path
        let repoPath = args.repo_root;
        if (!repoPath.startsWith('/')) {
          // Relative path - resolve from ~/dev
          repoPath = join(homedir(), 'dev', repoPath);
        }
        repoPath = FSUtils.expandPath(repoPath);
    
        // Ensure repo exists
        FSUtils.ensureDir(repoPath);
    
        // Check if PHASES.md exists
        const phasesPath = join(repoPath, 'PHASES.md');
        const phasesExist = FSUtils.fileExists(phasesPath);
    
        let phases: Array<{
          id: string;
          name: string;
          description: string;
          deliverables?: string[];
          done_criteria?: string[];
        }> = [];
    
        // If phases provided explicitly, use them
        if (args.phases && args.phases.length > 0) {
          phases = args.phases;
        } else {
          // Infer phases from repo structure
          const analysis = RepoAnalyzer.analyzeRepo(repoPath, {
            detect_frameworks: true,
            detect_infrastructure: true,
          });
    
          if (analysis.suggested_phases.length > 0) {
            phases = analysis.suggested_phases.map(p => ({
              id: p.id,
              name: p.name,
              description: p.description,
              deliverables: p.deliverables,
              done_criteria: p.done_criteria,
            }));
          } else {
            // Fallback to default phases
            phases = [
              {
                id: '01',
                name: 'foundation',
                description: 'Project foundation and setup',
                deliverables: ['Project structure', 'README.md', 'Basic configuration'],
                done_criteria: ['Project structure is in place', 'README.md exists'],
              },
            ];
          }
        }
    
        // Read existing phases if they exist and we're not overwriting
        let existingPhases: any = null;
        if (phasesExist && !args.overwrite_existing) {
          try {
            const existingContent = FSUtils.readFile(phasesPath);
            // Try to parse as markdown (we'll use a simple approach for now)
            // For full parsing, we'd use the RepoHandler
            existingPhases = { exists: true, content: existingContent };
          } catch (error) {
            errors.push(`Failed to read existing PHASES.md: ${error}`);
          }
        }
    
        // Generate PHASES.md content
        const now = new Date().toISOString();
        const phasesContent = generatePhasesMarkdown(
          args.project_name,
          phases,
          existingPhases ? now : now, // created_at
          now, // updated_at
          existingPhases ? 'updated' : 'created'
        );
    
        // Write PHASES.md
        try {
          FSUtils.writeFile(phasesPath, phasesContent);
          if (phasesExist) {
            filesUpdated.push(phasesPath);
          } else {
            filesWritten.push(phasesPath);
          }
        } catch (error) {
          errors.push(`Failed to write PHASES.md: ${error}`);
        }
    
        // Generate and write PHASE-XX.md files
        for (const phase of phases) {
          const phaseFilePath = join(repoPath, `PHASE-${phase.id.padStart(2, '0')}.md`);
          const phaseContent = generatePhaseFileContent(phase, args.project_name);
    
          try {
            const phaseExists = FSUtils.fileExists(phaseFilePath);
            FSUtils.writeFile(phaseFilePath, phaseContent);
            if (phaseExists) {
              filesUpdated.push(phaseFilePath);
            } else {
              filesWritten.push(phaseFilePath);
            }
          } catch (error) {
            errors.push(`Failed to write ${phaseFilePath}: ${error}`);
          }
        }
    
        const success = errors.length === 0;
        const message = success
          ? `${phasesExist ? 'Updated' : 'Created'} project "${args.project_name}" with ${phases.length} phase(s)`
          : `Partially ${phasesExist ? 'updated' : 'created'} project with ${errors.length} error(s)`;
    
        return {
          success,
          message,
          phases_discovered: phases.length,
          phases_created: phases.length,
          files_written: filesWritten,
          files_updated: filesUpdated,
          errors,
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          success: false,
          message: `Failed to plan project: ${errorMessage}`,
          phases_discovered: 0,
          phases_created: 0,
          files_written: [],
          files_updated: [],
          errors: [errorMessage],
        };
      }
    }
  • Tool factory function that returns the Tool object definition, including name, description, and detailed inputSchema for validation.
    export function createPlanProjectTool(phaseManager: PhaseManager): Tool {
      return {
        name: 'overseer.plan_project',
        description: 'Plan a new project by creating phase definitions. Creates PHASES.md and PHASE-*.md files in the repository. Can infer phases from project structure if not provided.',
        inputSchema: {
          type: 'object',
          properties: {
            repo_root: {
              type: 'string',
              description: 'Root path of the repository (absolute path or relative to ~/dev)',
            },
            project_name: {
              type: 'string',
              description: 'Name of the project',
            },
            project_summary: {
              type: 'string',
              description: 'Summary description of the project',
            },
            overwrite_existing: {
              type: 'boolean',
              default: false,
              description: 'If true, overwrite existing PHASES.md. If false, normalize and merge.',
            },
            phases: {
              type: 'array',
              items: {
                type: 'object',
                properties: {
                  id: { type: 'string' },
                  name: { type: 'string' },
                  description: { type: 'string' },
                  deliverables: {
                    type: 'array',
                    items: { type: 'string' },
                  },
                  done_criteria: {
                    type: 'array',
                    items: { type: 'string' },
                  },
                },
                required: ['id', 'name', 'description'],
              },
              description: 'Optional: Explicit phase definitions. If not provided, phases will be inferred.',
            },
          },
          required: ['repo_root', 'project_name'],
        },
      };
  • Tool handler registration in the central dispatcher switch statement within handleToolCall function.
    case 'overseer.plan_project':
      return await handlePlanProject(args, context.phaseManager);
  • Registration of the tool in the createTools array for inclusion in the tools list.
    createPlanProjectTool(context.phaseManager),
  • Helper function to generate the content for PHASES.md file from project phases.
    function generatePhasesMarkdown(
      projectName: string,
      phases: Array<{
        id: string;
        name: string;
        description: string;
        deliverables?: string[];
        done_criteria?: string[];
      }>,
      created_at: string,
      updated_at: string,
      action: 'created' | 'updated'
    ): string {
      const lines: string[] = [];
    
      lines.push(`# Project Phases: ${projectName}`);
      lines.push('');
      lines.push('This document tracks the phases of the project.');
      lines.push('');
      lines.push('## Metadata');
      lines.push('');
      lines.push(`- **Created**: ${created_at}`);
      lines.push(`- **Updated**: ${updated_at}`);
      lines.push(`- **Total Phases**: ${phases.length}`);
      lines.push('');
      lines.push('## Phases');
      lines.push('');
    
      phases.forEach((phase) => {
        lines.push(`### ${phase.id}. ${phase.name}`);
        lines.push('');
        lines.push(`**Status**: pending`);
        lines.push(`**Description**: ${phase.description}`);
        lines.push('');
    
        if (phase.deliverables && phase.deliverables.length > 0) {
          lines.push('**Deliverables**:');
          lines.push('');
          phase.deliverables.forEach(deliverable => {
            lines.push(`- ${deliverable}`);
          });
          lines.push('');
        }
    
        if (phase.done_criteria && phase.done_criteria.length > 0) {
          lines.push('**Done Criteria**:');
          lines.push('');
          phase.done_criteria.forEach(criterion => {
            lines.push(`- [ ] ${criterion}`);
          });
          lines.push('');
        }
      });
    
      lines.push('---');
      lines.push('');
      lines.push(`*This file is automatically managed by Overseer MCP.*`);
      lines.push(`*Last ${action}: ${updated_at}*`);
    
      return lines.join('\n');
    }

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