Skip to main content
Glama

CTX: Context as Code (CaC) tool

by context-hub
MIT License
235
  • Apple
  • Linux
ComposerSourceFetcher.php8.81 kB
<?php declare(strict_types=1); namespace Butschster\ContextGenerator\Source\Composer; use Butschster\ContextGenerator\Application\Logger\LoggerPrefix; use Butschster\ContextGenerator\Lib\Content\ContentBuilderFactory; use Butschster\ContextGenerator\Lib\Variable\VariableResolver; use Butschster\ContextGenerator\Modifier\ModifiersApplierInterface; use Butschster\ContextGenerator\Source\Composer\Provider\ComposerProviderInterface; use Butschster\ContextGenerator\Source\Fetcher\SourceFetcherInterface; use Butschster\ContextGenerator\Source\File\FileSource; use Butschster\ContextGenerator\Source\File\FileSourceFetcher; use Butschster\ContextGenerator\Source\File\SymfonyFinder; use Butschster\ContextGenerator\Source\SourceInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; /** * Fetcher for Composer package sources * @implements SourceFetcherInterface<ComposerSource> */ final readonly class ComposerSourceFetcher implements SourceFetcherInterface { private FileSourceFetcher $fileSourceFetcher; public function __construct( private ComposerProviderInterface $provider, SymfonyFinder $finder, private string $basePath = '.', private ContentBuilderFactory $builderFactory = new ContentBuilderFactory(), private VariableResolver $variableResolver = new VariableResolver(), #[LoggerPrefix(prefix: 'composer-source-fetcher')] private ?LoggerInterface $logger = null, ) { // Create a FileSourceFetcher to handle the actual file fetching $this->fileSourceFetcher = new FileSourceFetcher( basePath: $this->basePath, finder: $finder, builderFactory: $this->builderFactory, logger: $this->logger instanceof LoggerInterface ? $this->logger : new NullLogger(), ); } public function supports(SourceInterface $source): bool { $isSupported = $source instanceof ComposerSource; $this->logDebug('Checking if source is supported', [ 'sourceType' => $source::class, 'isSupported' => $isSupported, ]); return $isSupported; } public function fetch(SourceInterface $source, ModifiersApplierInterface $modifiersApplier): string { if (!$source instanceof ComposerSource) { $errorMessage = 'Source must be an instance of ComposerSource'; $this->logError($errorMessage, [ 'sourceType' => $source::class, ]); throw new \InvalidArgumentException($errorMessage); } $description = $this->variableResolver->resolve($source->getDescription()); $this->logInfo('Fetching Composer source content', [ 'description' => $description, 'composerPath' => $source->composerPath, 'includeDevDependencies' => $source->includeDevDependencies, ]); // Create a content builder $builder = $this->builderFactory ->create() ->addTitle($description); // Get packages from the provider $packages = $this->provider->getPackages( $source->composerPath, $source->includeDevDependencies, ); // Filter packages if packages is set $packages = $packages->filter($source->packages); if ($packages->count() === 0) { $this->logWarning('No matching packages found', [ 'composerPath' => $source->composerPath, 'packages' => $source->packages, ]); $builder->addText('No matching packages found.'); return $builder->build(); } $this->logInfo('Found matching packages', [ 'count' => $packages->count(), 'packages' => \array_keys($packages->all()), ]); // Generate a tree view of selected packages if requested if ($source->treeView->enabled) { $this->logDebug('Generating package tree view'); $builder->addTreeView($packages->generateTree()); } // For each package, fetch its source code foreach ($packages as $package) { $this->logInfo('Processing package', [ 'name' => $package->name, 'version' => $package->version, 'path' => $package->path, ]); $builder->addTitle(\sprintf('%s (%s)', $package->name, $package->version), 2); // Add package description and metadata if available if ($package->getDescription()) { $builder->addDescription($package->getDescription()); } // Create a metadata section with authors, license, homepage, etc. $metadata = []; if ($authors = $package->getFormattedAuthors()) { $metadata[] = "**Authors:** {$authors}"; } if ($license = $package->getFormattedLicense()) { $metadata[] = "**License:** {$license}"; } if ($homepage = $package->getHomepage()) { $metadata[] = "**Homepage:** {$homepage}"; } if (!empty($metadata)) { $builder->addText(\implode("\n", $metadata)); } // Get source directories for this package $sourceDirs = $package->getSourceDirectories(); $this->logDebug('Found source directories', [ 'package' => $package->name, 'directories' => $sourceDirs, ]); // For each source directory, create a FileSource and fetch its content foreach ($sourceDirs as $dir) { $sourceDir = $package->path . '/' . $dir; if (!\is_dir($sourceDir)) { $this->logWarning('Source directory not found', [ 'package' => $package->name, 'directory' => $sourceDir, ]); continue; } $this->logDebug('Creating FileSource for directory', [ 'package' => $package->name, 'directory' => $sourceDir, ]); // Create a FileSource for this directory $fileSource = new FileSource( sourcePaths: $sourceDir, filePattern: $source->filePattern, notPath: $source->notPath, path: $source->path, contains: $source->contains, notContains: $source->notContains, treeView: $source->treeView, // Show tree view for individual directories if requested modifiers: $source->modifiers, ); // Use the FileSourceFetcher to fetch the content try { $content = $this->fileSourceFetcher->fetch($fileSource, $modifiersApplier); $builder->addText($content); } catch (\Throwable $e) { $this->logError('Error fetching package source', [ 'package' => $package->name, 'directory' => $sourceDir, 'error' => $e->getMessage(), ]); $builder->addText( \sprintf( "Error fetching source for %s in directory %s: %s", $package->name, $sourceDir, $e->getMessage(), ), ); } } $builder->addSeparator(); } $content = $builder->build(); $this->logInfo('Composer source content fetched successfully', [ 'packageCount' => $packages->count(), 'contentLength' => \strlen($content), ]); return $content; } /** * Log a debug message if a logger is available */ private function logDebug(string $message, array $context = []): void { $this->logger?->debug($message, $context); } /** * Log an info message if a logger is available */ private function logInfo(string $message, array $context = []): void { $this->logger?->info($message, $context); } /** * Log a warning message if a logger is available */ private function logWarning(string $message, array $context = []): void { $this->logger?->warning($message, $context); } /** * Log an error message if a logger is available */ private function logError(string $message, array $context = []): void { $this->logger?->error($message, $context); } }

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/context-hub/generator'

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