Console
Melodic provides a lightweight console runner for building CLI commands, plus a scaffolding CLI for generating projects and CQRS entity stacks. The system includes the melodic binary with scaffolding commands, a Console runner, a Command base class with output helpers, and built-in commands for listing routes and clearing the cache.
The melodic CLI
The framework ships with a bin/melodic binary that provides project and entity scaffolding. After installing the framework via Composer, run it with vendor/bin/melodic:
$ vendor/bin/melodic
Melodic 1.7.2
Available commands:
make:project Create a new Melodic project (full, api, or mvc)
make:entity Generate CQRS entity files (DTO, queries, commands, service, controller)
make:config Create an environment configuration file
claude:install Install Claude Code agents and skills into your project
make:project
Creates a new Melodic project with the canonical directory structure, configuration, entry points, a service provider, and a console script.
# Create a full project — MVC + API (default)
vendor/bin/melodic make:project my-app
# API-only project
vendor/bin/melodic make:project my-api --type=api
# MVC-only project
vendor/bin/melodic make:project my-site --type=mvc
Project Types
| Type | Flag | Description |
|---|---|---|
| Full (default) | --type=full or omit | MVC views + API routing — recommended for most apps |
| API only | --type=api | No views directory, API-only index.php |
| MVC only | --type=mvc | Views + HomeController, MVC-only index.php |
The default full type generates the following structure:
my-app/
composer.json PSR-4 autoloading configured
config/
config.json Default configuration (database, JWT, CORS)
public/
index.php HTTP entry point (MVC + API routes)
.htaccess Apache rewrite rules
bin/
console CLI entry point
src/
Controllers/
Services/
DTO/
Data/
Middleware/
Providers/
AppServiceProvider.php Ready-to-use service provider
views/
layouts/
main.phtml Base HTML layout
home/
index.phtml Home page template
storage/
cache/
logs/
tests/
.gitignore
API-only projects omit the views/ directory and HomeController. MVC-only projects include views but use an MVC-only index.php without API route stubs.
After generating, follow the printed instructions:
$ vendor/bin/melodic make:project my-app
Creating full project 'my-app'...
Project 'my-app' created successfully!
Next steps:
cd my-app
composer install
php -S localhost:8080 -t public
make:entity
Generates a full CQRS entity stack — 8 files per entity — following the framework's naming conventions. Run this from inside your project directory:
cd my-api
vendor/bin/melodic make:entity Church
This generates:
| File | Purpose |
|---|---|
src/DTO/ChurchModel.php |
Data transfer object extending Model |
src/Data/Church/Queries/GetAllChurchesQuery.php |
Query to fetch all records |
src/Data/Church/Queries/GetChurchByIdQuery.php |
Query to fetch a single record by ID |
src/Data/Church/Commands/CreateChurchCommand.php |
Command to insert a record |
src/Data/Church/Commands/UpdateChurchCommand.php |
Command to update a record |
src/Data/Church/Commands/DeleteChurchCommand.php |
Command to delete a record |
src/Services/ChurchService.php |
Service orchestrating queries and commands |
src/Controllers/ChurchController.php |
API controller with full CRUD actions |
Safe to re-run. Existing files are never overwritten. The command skips any file that already exists and reports which files were created or skipped.
The entity name is automatically converted to the appropriate casing:
| Input | Entity | Plural | Table Name |
|---|---|---|---|
Church | Church | Churches | churches |
blog-post | BlogPost | BlogPosts | blog_posts |
user_role | UserRole | UserRoles | user_roles |
The namespace is read from your project's composer.json PSR-4 autoload configuration.
Tip: After generating, fill in the SQL statements and model properties in the generated files, then register the entity's routes in your public/index.php:
$router->apiResource('/api/churches', ChurchController::class);
make:config
Creates an environment configuration file with a minimal template. Use this to scaffold config overrides for a new environment:
vendor/bin/melodic make:config staging
This creates config/config.staging.json with sensible defaults. The command refuses to overwrite an existing file, so it is safe to run even if you are unsure whether the file already exists.
The generated file contains a minimal template that you can customize for your environment:
{
"app": {
"debug": false
},
"database": {
"dsn": ""
},
"jwt": {
"secret": "",
"algorithm": "HS256"
}
}
Once created, the file is automatically loaded by loadEnvironmentConfig() when the APP_ENV environment variable matches the environment name. See the Configuration docs for details on the loading order.
claude:install
Installs Melodic-specific Claude Code agents and skills into your project. This sets up AI-assisted development tooling that understands the framework's architecture, conventions, and patterns.
vendor/bin/melodic claude:install
This installs:
| Type | Name | Purpose |
|---|---|---|
| Agent | melodic-expert | Framework expert for architecture, patterns, and debugging |
| Skill | /melodic:scaffold-app | Scaffold a new Melodic application |
| Skill | /melodic:scaffold-resource | Scaffold a CQRS resource (Model, Queries, Commands, Service, Controller) |
| Skill | /melodic:add-middleware | Scaffold a middleware class |
The command also generates a CLAUDE.md file at your project root with framework conventions, naming patterns, and architecture documentation for Claude Code to reference.
Safe to re-run. Existing files are never overwritten. Use --force to replace previously installed files: vendor/bin/melodic claude:install --force
Console Runner
The Melodic\Console\Console class is the entry point for CLI applications. It manages command registration, dispatches to the correct command based on $argv, and provides built-in help output.
<?php
use Melodic\Console\Console;
$console = new Console();
$console->setName('My App');
$console->register($routeListCommand);
$console->register($cacheClearCommand);
exit($console->run($argv));
Version tracking. The Console class automatically reads the version from Melodic\Framework::VERSION. You can override it with setVersion() if your application tracks its own version separately.
How Dispatching Works
The run() method reads the command name from $argv[1]. Any arguments after the command name are passed to the command's execute() method as a string array. The method returns an integer exit code.
| Input | Behavior | Exit Code |
|---|---|---|
| No arguments | Shows help listing | 0 |
help |
Shows help listing | 0 |
| Valid command name | Executes the command | Returned by the command |
| Unknown command name | Prints error, shows help listing | 1 |
Help Output
When run with no arguments or the help command, the console prints the application name, version, and a list of all registered commands with their descriptions:
$ php bin/console
My App 1.7.2
Available commands:
route:list List all registered routes
cache:clear Clear the application cache
CommandInterface
The Melodic\Console\CommandInterface defines the contract that all commands must implement. You can implement this interface directly for full control, or extend the Command base class for convenience.
<?php
use Melodic\Console\CommandInterface;
interface CommandInterface
{
public function getName(): string;
public function getDescription(): string;
public function execute(array $args): int;
}
| Method | Return Type | Description |
|---|---|---|
getName() |
string |
The command name used on the CLI (e.g., 'route:list') |
getDescription() |
string |
A short description shown in the help listing |
execute() |
int |
Run the command logic; return 0 for success, non-zero for failure |
Command Base Class
The Melodic\Console\Command abstract class implements CommandInterface and provides the constructor and output helpers. Extend this class for most commands.
<?php
use Melodic\Console\Command;
abstract class Command implements CommandInterface
{
public function __construct(
private readonly string $name,
private readonly string $description,
) {}
// Output helpers below...
}
Output Helpers
The base class provides four output methods for writing to the terminal.
| Method | Signature | Description |
|---|---|---|
writeln() |
writeln(string $text): void |
Write text followed by a newline to STDOUT |
write() |
write(string $text): void |
Write text to STDOUT without a trailing newline |
error() |
error(string $text): void |
Write text followed by a newline to STDERR |
table() |
table(array $headers, array $rows): void |
Render an ASCII table with borders to STDOUT |
Table Output
The table() method renders a formatted ASCII table with automatic column width calculation. Column widths are determined by the longest value in each column (including headers).
<?php
$this->table(
['Name', 'Email', 'Role'],
[
['Alice', 'alice@example.com', 'Admin'],
['Bob', 'bob@example.com', 'Editor'],
],
);
Produces:
+-------+-------------------+--------+
| Name | Email | Role |
+-------+-------------------+--------+
| Alice | alice@example.com | Admin |
| Bob | bob@example.com | Editor |
+-------+-------------------+--------+
Built-in Commands
Melodic ships with two built-in commands that you can register immediately.
route:list
The Melodic\Console\RouteListCommand displays all registered routes in a table with the HTTP method, path, controller class, and action method.
<?php
use Melodic\Console\RouteListCommand;
use Melodic\Routing\Router;
$router = $container->get(Router::class);
$console->register(new RouteListCommand($router));
Running php bin/console route:list produces:
+--------+------------------+--------------------+--------+
| Method | Path | Controller | Action |
+--------+------------------+--------------------+--------+
| GET | / | HomeController | index |
| GET | /api/users | UserController | index |
| POST | /api/users | UserController | store |
| GET | /api/users/{id} | UserController | show |
| PUT | /api/users/{id} | UserController | update |
| DELETE | /api/users/{id} | UserController | delete |
+--------+------------------+--------------------+--------+
Debugging routes. Use route:list to verify that your route registrations, groups, and API resources are resolving correctly. It reads directly from the Router instance.
cache:clear
The Melodic\Console\CacheClearCommand calls clear() on the injected CacheInterface to remove all cached entries.
<?php
use Melodic\Console\CacheClearCommand;
use Melodic\Cache\CacheInterface;
$cache = $container->get(CacheInterface::class);
$console->register(new CacheClearCommand($cache));
$ php bin/console cache:clear
Cache cleared successfully.
The command returns exit code 0 on success. If clear() returns false, it writes an error to STDERR and returns exit code 1.
Creating Custom Commands
To create a custom command, extend Melodic\Console\Command, call the parent constructor with a name and description, and implement the execute() method.
Example: GreetCommand
<?php
declare(strict_types=1);
namespace App\Commands;
use Melodic\Console\Command;
class GreetCommand extends Command
{
public function __construct()
{
parent::__construct('greet', 'Greet a user by name');
}
public function execute(array $args): int
{
$name = $args[0] ?? 'World';
$this->writeln("Hello, {$name}!");
return 0;
}
}
$ php bin/console greet Alice
Hello, Alice!
$ php bin/console greet
Hello, World!
Arguments. The $args array contains everything after the command name. For php bin/console greet Alice --loud, the array would be ['Alice', '--loud']. Argument parsing is left to your command — there is no built-in flag parser.
Example: Command with Table Output
<?php
declare(strict_types=1);
namespace App\Commands;
use Melodic\Console\Command;
class UserListCommand extends Command
{
public function __construct(
private readonly UserService $userService,
) {
parent::__construct('user:list', 'List all users');
}
public function execute(array $args): int
{
$users = $this->userService->getAll();
if (count($users) === 0) {
$this->writeln('No users found.');
return 0;
}
$this->table(
['ID', 'Name', 'Email'],
array_map(fn($u) => [(string) $u->id, $u->name, $u->email], $users),
);
return 0;
}
}
Example: Command with Error Handling
<?php
declare(strict_types=1);
namespace App\Commands;
use Melodic\Console\Command;
class ImportCommand extends Command
{
public function __construct()
{
parent::__construct('data:import', 'Import data from a CSV file');
}
public function execute(array $args): int
{
if (empty($args[0])) {
$this->error('Usage: data:import <filename>');
return 1;
}
$filename = $args[0];
if (!file_exists($filename)) {
$this->error("File not found: {$filename}");
return 1;
}
$this->writeln("Importing from {$filename}...");
// ... import logic ...
$this->writeln('Import complete.');
return 0;
}
}
Setting Up a Console Entry Point
Create a bin/console script at your project root to serve as the CLI entry point. This script bootstraps the application, registers commands, and runs the console.
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Melodic\Core\Application;
use Melodic\Console\Console;
use Melodic\Console\RouteListCommand;
use Melodic\Console\CacheClearCommand;
use Melodic\Routing\Router;
use Melodic\Cache\CacheInterface;
// Bootstrap the application
$app = new Application(dirname(__DIR__));
$app->loadEnvironmentConfig();
$app->services(function ($container) {
// Register your service providers...
});
$app->routes(function ($router) {
// Register your routes (needed for route:list)...
});
// Build the console
$console = new Console();
$console->setName('My App');
$console->setVersion('1.0.0');
// Register built-in commands
$container = $app->getContainer();
$console->register(new RouteListCommand($container->get(Router::class)));
$console->register(new CacheClearCommand($container->get(CacheInterface::class)));
// Register custom commands
$console->register(new App\Commands\GreetCommand());
exit($console->run($argv));
Make it executable. After creating bin/console, run chmod +x bin/console so you can invoke it directly as ./bin/console instead of php bin/console.
Project Structure
my-app/
bin/
console ← CLI entry point
config/
config.json
public/
index.php ← HTTP entry point
src/
Commands/
GreetCommand.php
UserListCommand.php
vendor/
composer.json
Console API Reference
Console Class
| Method | Signature | Description |
|---|---|---|
register() |
register(CommandInterface $command): void |
Register a command with the console |
setName() |
setName(string $name): void |
Set the application name shown in help output |
setVersion() |
setVersion(string $version): void |
Set the version string shown in help output |
run() |
run(array $argv): int |
Parse argv, dispatch to a command, and return its exit code |
Command Base Class
| Method | Visibility | Description |
|---|---|---|
__construct(string $name, string $description) |
public | Set the command name and description |
getName(): string |
public | Return the command name |
getDescription(): string |
public | Return the command description |
execute(array $args): int |
public (abstract) | Implement command logic; return exit code |
writeln(string $text): void |
protected | Write to STDOUT with newline |
write(string $text): void |
protected | Write to STDOUT without newline |
error(string $text): void |
protected | Write to STDERR with newline |
table(array $headers, array $rows): void |
protected | Render an ASCII table with auto-sized columns |
Class Reference
| Class | Namespace | Purpose |
|---|---|---|
Console |
Melodic\Console |
CLI runner: registers commands, parses argv, dispatches execution |
CommandInterface |
Melodic\Console |
Contract for all console commands |
Command |
Melodic\Console |
Abstract base class with name, description, and output helpers |
RouteListCommand |
Melodic\Console |
Built-in command that lists all registered routes in a table |
CacheClearCommand |
Melodic\Console |
Built-in command that clears the application cache |
MakeProjectCommand |
Melodic\Console\Make |
Scaffolds a new API or MVC project with canonical directory structure |
MakeEntityCommand |
Melodic\Console\Make |
Generates 8 CQRS files for an entity (DTO, queries, commands, service, controller) |
MakeConfigCommand |
Melodic\Console\Make |
Creates an environment configuration file with a minimal template |
Stub |
Melodic\Console\Make |
Template rendering and string utilities (pascalCase, camelCase, snakeCase, pluralize) |
ClaudeInstallCommand |
Melodic\Console |
Installs Claude Code agents and skills into a Melodic project |