ListPromptsCommand.php•8.32 kB
<?php
declare(strict_types=1);
namespace Butschster\ContextGenerator\McpServer\Prompt\Console;
use Butschster\ContextGenerator\Application\AppScope;
use Butschster\ContextGenerator\Config\ConfigurationProvider;
use Butschster\ContextGenerator\Config\Exception\ConfigLoaderException;
use Butschster\ContextGenerator\Console\BaseCommand;
use Butschster\ContextGenerator\Console\Renderer\Style;
use Butschster\ContextGenerator\DirectoriesInterface;
use Butschster\ContextGenerator\McpServer\Prompt\Extension\PromptDefinition;
use Butschster\ContextGenerator\McpServer\Prompt\Filter\FilterStrategy;
use Butschster\ContextGenerator\McpServer\Prompt\Filter\PromptFilterFactory;
use Butschster\ContextGenerator\McpServer\Prompt\Filter\PromptFilterInterface;
use Butschster\ContextGenerator\McpServer\Prompt\PromptProviderInterface;
use Butschster\ContextGenerator\McpServer\Prompt\PromptType;
use PhpMcp\Schema\Prompt;
use Spiral\Console\Attribute\Option;
use Spiral\Core\Container;
use Spiral\Core\Scope;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableCellStyle;
use Symfony\Component\Console\Helper\TableSeparator;
#[AsCommand(
name: 'prompts:list',
description: 'List all available prompts with their details',
aliases: ['prompts'],
)]
final class ListPromptsCommand extends BaseCommand
{
#[Option(
name: 'config-file',
shortcut: 'c',
description: 'Path to configuration file (absolute or relative to current directory).',
)]
protected ?string $configPath = null;
#[Option(
name: 'tag',
shortcut: 't',
description: 'Filter prompts by tag (can be used multiple times)',
)]
protected array $tags = [];
#[Option(
name: 'exclude-tag',
shortcut: 'x',
description: 'Exclude prompts with specific tag (can be used multiple times)',
)]
protected array $excludeTags = [];
#[Option(
name: 'id',
shortcut: 'p',
description: 'Filter prompts by ID (can be used multiple times)',
)]
protected array $promptIds = [];
#[Option(
name: 'detailed',
shortcut: 'd',
description: 'Show detailed information including arguments',
)]
protected bool $detailed = false;
public function __invoke(Container $container, DirectoriesInterface $dirs): int
{
return $container->runScope(
bindings: new Scope(
name: AppScope::Compiler,
bindings: [
DirectoriesInterface::class => $dirs->determineRootPath($this->configPath),
],
),
scope: function (
ConfigurationProvider $configProvider,
PromptProviderInterface $promptProvider,
PromptFilterFactory $filterFactory,
) {
try {
// Get the appropriate loader based on options provided
if ($this->configPath !== null) {
$this->logger->info(\sprintf('Loading configuration from %s...', $this->configPath));
$loader = $configProvider->fromPath($this->configPath);
} else {
$this->logger->info('Loading configuration from default location...');
$loader = $configProvider->fromDefaultLocation();
}
} catch (ConfigLoaderException $e) {
$this->logger->error('Failed to load configuration', [
'error' => $e->getMessage(),
]);
$this->output->error(\sprintf('Failed to load configuration: %s', $e->getMessage()));
return Command::FAILURE;
}
// Load configuration to make sure all prompts are properly registered
$loader->load();
// Get all prompts
$prompts = $promptProvider->all();
// Create filter based on command options
$filter = $this->createFilter($filterFactory);
// Apply filter if needed
if ($filter !== null) {
$filteredPrompts = [];
foreach ($prompts as $id => $promptDef) {
$promptConfig = [
'id' => $promptDef->id,
'tags' => $promptDef->tags,
];
if ($filter->shouldInclude($promptConfig)) {
$filteredPrompts[$id] = $promptDef;
}
}
$prompts = $filteredPrompts;
}
if (empty($prompts)) {
$this->output->warning('No prompts found matching the specified criteria.');
return Command::SUCCESS;
}
// Display prompts based on the selected format
return $this->displayAsTable($prompts);
},
);
}
/**
* Creates a filter based on command options.
*/
private function createFilter(PromptFilterFactory $filterFactory): ?PromptFilterInterface
{
$filterConfig = [];
// Add ID filter if provided
if (!empty($this->promptIds)) {
$filterConfig['ids'] = $this->promptIds;
}
// Add tag filters if provided
if (!empty($this->tags) || !empty($this->excludeTags)) {
$filterConfig['tags'] = [];
if (!empty($this->tags)) {
$filterConfig['tags']['include'] = $this->tags;
$filterConfig['tags']['match'] = FilterStrategy::ANY->value;
}
if (!empty($this->excludeTags)) {
$filterConfig['tags']['exclude'] = $this->excludeTags;
}
}
return $filterFactory->createFromConfig($filterConfig);
}
/**
* Displays prompts as a table.
* @param array<PromptDefinition> $prompts
*/
private function displayAsTable(array $prompts): int
{
$title = 'Available Prompts';
$this->output->title($title);
$table = new Table($this->output);
if ($this->detailed) {
$table->setHeaders(['ID', 'Type', 'Description', 'Tags', 'Arguments']);
} else {
$table->setHeaders(['ID', 'Type', 'Description', 'Tags']);
}
foreach ($prompts as $promptDef) {
$row = [
new TableCell($promptDef->id, ['style' => new TableCellStyle(['fg' => 'cyan'])]),
new TableCell(
$promptDef->type->value,
[
'style' => new TableCellStyle(
['fg' => $promptDef->type === PromptType::Prompt ? 'green' : 'blue'],
),
],
),
$promptDef->prompt->description ?? '-',
\implode(', ', $promptDef->tags),
];
if ($this->detailed) {
$args = $this->formatArguments($promptDef->prompt);
$row[] = $args;
}
$table->addRow($row);
if ($this->detailed) {
$table->addRow(new TableSeparator());
}
}
$table->render();
$this->output->writeln('');
$this->output->writeln(\sprintf('%s: %s', Style::property('Total prompts'), Style::count(\count($prompts))));
return Command::SUCCESS;
}
/**
* Formats arguments for display.
*/
private function formatArguments(Prompt $prompt): string
{
if (empty($prompt->arguments)) {
return '-';
}
$args = [];
foreach ($prompt->arguments as $arg) {
$name = $arg->name;
if ($arg->required) {
$name .= '*';
}
if ($arg->description) {
$name .= \sprintf(' (%s)', $arg->description);
}
$args[] = $name;
}
return \implode("\n", $args);
}
}