From redaxo-core
Builds Symfony Console commands for REDAXO addons using rex_console_command. Covers file structure, class naming, package.yml registration, and SymfonyStyle helpers. Use for CLI tools, diagnostics, cronjobs, replacing standalone scripts.
How this skill is triggered — by the user, by Claude, or both
Slash command
/redaxo-core:redaxo-console-commandsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
For any CLI / diagnostic / maintenance task in an addon, build a **console command** — not a standalone PHP bootstrap script. REDAXO uses Symfony Console under the hood; the bootstrap (autoloading, addon init, package order) only works correctly through `bin/console`.
For any CLI / diagnostic / maintenance task in an addon, build a console command — not a standalone PHP bootstrap script. REDAXO uses Symfony Console under the hood; the bootstrap (autoloading, addon init, package order) only works correctly through bin/console.
// bin/_my_check.php — WRONG
require __DIR__ . '/../src/core/boot.php'; // missing path provider
$client = rex_elasticsearch_client::factory(); // wrong API
Standalone scripts like this either fail to boot, or end up using APIs that don't exist. Even if you get them working, they're invisible in bin/console list, can't run as cronjobs, and aren't reviewable as part of the addon.
src/addons/<addon>/lib/command/<name>.phprex_<addon>_command_<name> — subcommand : becomes _ (e.g. command ndcg:detail → class rex_elasticsearchtools_command_ndcg_detail)rex_console_command (REDAXO's wrapper around Symfony\Component\Console\Command\Command)package.yml under console_commands: — without this REDAXO won't expose the command. Autoloading alone is not enough.<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Usage:
* bin/console addon:topic:action --option=value
*/
final class rex_addon_command_topic_action extends rex_console_command
{
protected function configure(): void
{
$this
->setDescription('One-line description shown in `bin/console list`')
->addOption('alias', 'a', InputOption::VALUE_REQUIRED, 'Index alias', 'default_alias')
->addOption('query', null, InputOption::VALUE_REQUIRED, 'Search query', 'test');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = $this->getStyle($input, $output); // SymfonyStyle helper
$io->title('My Command');
$alias = (string) $input->getOption('alias');
// SQL access works as usual
$sql = rex_sql::factory();
$rows = $sql->getArray(
'SELECT COUNT(*) AS c FROM ' . rex::getTable('table') . ' WHERE state = 7'
);
$io->table(['Header'], [['row1'], ['row2']]);
$io->success('Done');
return 0; // 0 = success, non-zero = failure
}
}
package.ymlconsole_commands:
addon:topic:action: rex_addon_command_topic_action
addon:topic:other: rex_addon_command_topic_other
The command name uses : as namespace separator. The class name uses _ everywhere.
$io)$this->getStyle($input, $output) returns a SymfonyStyle instance:
| Method | Use for |
|---|---|
$io->title($t) | Big heading |
$io->section($t) | Subheading |
$io->writeln($msg) | Plain output |
$io->note($msg) | Informational box |
$io->success($msg) | Green success box |
$io->error($msg) | Red error box |
$io->warning($msg) | Yellow warning box |
$io->table($headers, $rows) | Formatted table |
$io->progressStart($total) / progressAdvance() / progressFinish() | Progress bar |
$io->ask($q) / confirm($q) / choice($q, $opts) | Interactive prompts |
bin/console list | grep <addon>
If the command isn't there, the package.yml entry is missing or the class name doesn't match the file path / case.
$items = MyTable::query()->where('status', 1)->find();
foreach ($items as $item) {
$io->writeln($item->getValue('name'));
}
In package.yml of the cronjob addon's host configuration (or via the cronjob backend UI), schedule a system command:
bin/console addon:topic:action --alias=production
console_commands: entry in package.yml – the file is autoloaded but never registered, so bin/console list doesn't show it.MyAddonCommandThing instead of rex_my_addon_command_thing) – autoload silently fails.execute() – Symfony Console treats anything other than 0 (or the Command::SUCCESS/FAILURE constants on newer versions) as a failure.echo instead of $output->writeln() / $io->writeln() – Symfony's --quiet flag has no effect.setDescription() – bin/console list shows no help text, future-you won't remember what it does.rex::getUser() – there's no logged-in user in CLI context. It returns null. For permission checks, gate on --user flags or environment.Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub friendsofredaxo/claude-marketplace --plugin redaxo-core