checkpoint
Create a git commit with your changes to save work progress. Use this tool after completing work but before ending a session to generate a clean commit for review.
Instructions
Create a git commit with your changes. Call this AFTER completing your work but BEFORE end_session. Creates a clean commit for the user to review.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| repo | Yes | Repository name | |
| title | Yes | Commit title (max 50 chars) - REQUIRED. Be specific about what was changed. | |
| message | No | Detailed commit message (optional, max 1000 chars) | |
| author | No | Author name (e.g., "Claude", "GPT-4"). Defaults to "AI Assistant" |
Implementation Reference
- Main handler logic for the 'checkpoint' tool. Validates input, checks for changes, stages all files with 'git add -A', creates a git commit with custom message and author, returns detailed success response with commit hash and change summary.async handle(args: unknown): Promise<MCPToolResponse> { if (!this.isValidArgs(args)) { return createErrorResponse( "Error: Both 'repo' and 'title' parameters are required.", `Example usage: checkpoint({ repo: "my-project", title: "Fix authentication bug", author: "Claude" }) Use list_repos() to see available repositories.` ); } // Validate title length if (args.title.length > 50) { return createErrorResponse( `Error: Title must be 50 characters or less (current: ${args.title.length} chars).` ); } // Validate message length if provided if (args.message && args.message.length > 1000) { return createErrorResponse( `Error: Message must be 1000 characters or less (current: ${args.message.length} chars).` ); } const repoPath = this.repositoryManager.resolveRepoPath(args.repo); if (!repoPath) { const repos = this.repositoryManager.getRepositories(); if (repos.length === 0) { return createErrorResponse( 'Error: No repositories found. Please add repositories to ShadowGit first.' ); } return createErrorResponse( `Error: Repository '${args.repo}' not found.`, `Available repositories: ${repos.map(r => ` - ${r.name}: ${r.path}`).join('\n')}` ); } // Check for changes const statusOutput = await this.gitExecutor.execute(['status', '--porcelain'], repoPath, true); if (!statusOutput || statusOutput.trim() === '' || statusOutput === '(empty output)') { return createErrorResponse( `β **No Changes Detected** ${'='.repeat(50)} π Repository has no changes to commit. β οΈ **Important:** Do NOT call end_session() - no commit was created. π‘ **NEXT STEP:** Make some changes first, then call checkpoint() again.` ); } // Build commit message const commitTitle = args.title; const commitBody = args.message || ''; const author = args.author || 'AI Assistant'; // Add all changes const addOutput = await this.gitExecutor.execute(['add', '-A'], repoPath, true); if (addOutput.startsWith('Error:')) { return createErrorResponse( `β **Failed to Stage Changes** ${'='.repeat(50)} π¨ **Error:** ${addOutput} β οΈ **Important:** Do NOT call end_session() - commit was not created. π‘ **NEXT STEP:** Check the error and try again.` ); } // Build full commit message let fullMessage = commitTitle; if (commitBody) { fullMessage += `\n\n${commitBody}`; } fullMessage += `\n\nAuthor: ${author} (via ShadowGit MCP)`; // Create commit with author information const commitEnv = { GIT_AUTHOR_NAME: author, GIT_AUTHOR_EMAIL: `${author.toLowerCase().replace(/\s+/g, '-')}@shadowgit.local`, GIT_COMMITTER_NAME: 'ShadowGit MCP', GIT_COMMITTER_EMAIL: 'shadowgit-mcp@shadowgit.local' }; // Use array-based command to avoid parsing issues const commitOutput = await this.gitExecutor.execute( ['commit', '-m', fullMessage], repoPath, true, commitEnv ); if (commitOutput.startsWith('Error:')) { return createErrorResponse( `β **Failed to Create Commit** ${'='.repeat(50)} π¨ **Error:** ${commitOutput} β οΈ **Important:** Do NOT call end_session() - commit was not created. π‘ **NEXT STEP:** Check the error message and try checkpoint() again.` ); } // Extract commit hash from output let commitHash: string | undefined; const hashMatch = commitOutput.match(/\[[\w\-]+ ([a-f0-9]+)\]/); if (hashMatch) { commitHash = hashMatch[1]; } // Get summary of changes const showOutput = await this.gitExecutor.execute(['show', '--stat', '--format=short', 'HEAD'], repoPath, true); return { content: [ { type: 'text', text: `β **Checkpoint Created Successfully!** ${'='.repeat(50)} π¦ **Commit Details:** ${commitOutput} π **Changes Summary:** ${showOutput} π **Commit Hash:** \`${commitHash || 'unknown'}\` ${'='.repeat(50)} π **Workflow Progress:** - [x] Session started - [x] Changes made - [x] Checkpoint created β¨ - [ ] Session ended π¨ **REQUIRED NEXT STEP:** You MUST now call \`end_session()\` to resume auto-commits: \`\`\`javascript end_session({ sessionId: "your-session-id", commitHash: "${commitHash || 'unknown'}" }) \`\`\` β οΈ **Important:** Only call end_session() because the commit was SUCCESSFUL.` } ], success: true }; }
- src/shadowgit-mcp-server.ts:115-140 (registration)Tool registration in ListToolsRequestSchema handler, defining name, description, and input schema for the 'checkpoint' tool.{ name: 'checkpoint', description: 'Create a git commit with your changes. Call this AFTER completing your work but BEFORE end_session. Creates a clean commit for the user to review.', inputSchema: { type: 'object', properties: { repo: { type: 'string', description: 'Repository name', }, title: { type: 'string', description: 'Commit title (max 50 chars) - REQUIRED. Be specific about what was changed.', }, message: { type: 'string', description: 'Detailed commit message (optional, max 1000 chars)', }, author: { type: 'string', description: 'Author name (e.g., "Claude", "GPT-4"). Defaults to "AI Assistant"', }, }, required: ['repo', 'title'], }, },
- src/shadowgit-mcp-server.ts:179-181 (registration)Switch case in CallToolRequestSchema handler that routes 'checkpoint' calls to CheckpointHandler.handle().case 'checkpoint': return await this.checkpointHandler.handle(args);
- src/types.ts:15-20 (schema)TypeScript interface defining the expected arguments for the checkpoint tool, used for validation in the handler.export interface ManualCheckpointArgs { repo: string; title: string; message?: string; author?: string; }
- Helper method for validating checkpoint arguments against ManualCheckpointArgs type.private isValidArgs(args: unknown): args is ManualCheckpointArgs { return ( typeof args === 'object' && args !== null && 'repo' in args && 'title' in args && typeof (args as ManualCheckpointArgs).repo === 'string' && typeof (args as ManualCheckpointArgs).title === 'string' ); }