GitStatusAction.php•4.12 kB
<?php
declare(strict_types=1);
namespace Butschster\ContextGenerator\McpServer\Action\Tools\Git;
use Butschster\ContextGenerator\DirectoriesInterface;
use Butschster\ContextGenerator\Lib\Git\Command;
use Butschster\ContextGenerator\Lib\Git\CommandsExecutorInterface;
use Butschster\ContextGenerator\Lib\Git\Exception\GitCommandException;
use Butschster\ContextGenerator\McpServer\Action\ToolResult;
use Butschster\ContextGenerator\McpServer\Action\Tools\Git\Dto\GitStatusFormat;
use Butschster\ContextGenerator\McpServer\Action\Tools\Git\Dto\GitStatusRequest;
use Butschster\ContextGenerator\McpServer\Attribute\InputSchema;
use Butschster\ContextGenerator\McpServer\Attribute\Tool;
use Butschster\ContextGenerator\McpServer\Routing\Attribute\Post;
use PhpMcp\Schema\Result\CallToolResult;
use Psr\Log\LoggerInterface;
#[Tool(
name: 'git-status',
description: 'Show the working tree status - displays paths that have differences between the index and the working tree, between the index and HEAD, and paths that are untracked',
title: 'Git Status',
)]
#[InputSchema(class: GitStatusRequest::class)]
final readonly class GitStatusAction
{
public function __construct(
private LoggerInterface $logger,
private CommandsExecutorInterface $commandsExecutor,
private DirectoriesInterface $dirs,
) {}
#[Post(path: '/tools/call/git-status', name: 'tools.git.status')]
public function __invoke(GitStatusRequest $request): CallToolResult
{
$this->logger->info('Processing git-status tool');
$repository = (string) $this->dirs->getRootPath();
// Check if we're in a valid git repository
if (!$this->commandsExecutor->isValidRepository($repository)) {
return ToolResult::error('Not a git repository (or any of the parent directories)');
}
try {
$commandParts = ['status'];
// Add format options
switch ($request->format) {
case GitStatusFormat::Short:
$commandParts[] = '--short';
break;
case GitStatusFormat::Porcelain:
$commandParts[] = '--porcelain';
break;
case GitStatusFormat::Long:
// Default format, no additional flags needed
break;
}
// Handle untracked files
if ($request->showUntracked) {
$commandParts[] = '--untracked-files=all';
} else {
$commandParts[] = '--untracked-files=no';
}
$command = new Command($repository, $commandParts);
$result = $this->commandsExecutor->executeString($command);
// If result is empty, indicate clean working tree
if (empty(\trim($result))) {
$result = \sprintf(
'On branch %s nothing to commit, working tree clean',
$this->getCurrentBranch(
$repository,
),
);
}
return ToolResult::text($result);
} catch (GitCommandException $e) {
$this->logger->error('Error executing git status', [
'repository' => $repository,
'error' => $e->getMessage(),
'code' => $e->getCode(),
]);
return ToolResult::error($e->getMessage());
} catch (\Throwable $e) {
$this->logger->error('Unexpected error during git status', [
'repository' => $repository,
'error' => $e->getMessage(),
]);
return ToolResult::error($e->getMessage());
}
}
private function getCurrentBranch(string $repository): string
{
try {
$command = new Command($repository, ['branch', '--show-current']);
$branch = \trim($this->commandsExecutor->executeString($command));
return !empty($branch) ? $branch : 'HEAD';
} catch (\Throwable) {
return 'HEAD';
}
}
}