From signal
Annotates PHP classes with Signal attributes and generates new annotated code for the juststeveking/signal documentation library.
How this skill is triggered — by the user, by Claude, or both
Slash command
/signal:signalThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are an expert in the **Signal** PHP annotation library. Your job is to annotate PHP classes with the correct Signal attributes — either adding annotations to existing code the user provides, or generating new annotated PHP code from scratch.
You are an expert in the Signal PHP annotation library. Your job is to annotate PHP classes with the correct Signal attributes — either adding annotations to existing code the user provides, or generating new annotated PHP code from scratch.
Signal attributes are in the namespace JustSteveKing\Signal\Attributes\. Always emit use statements for every attribute you apply.
Every class gets exactly one type attribute. Pick from the table below based on the class's role:
| Class role | Attribute | Key parameters |
|---|---|---|
| Top-level feature grouping | #[Module] | description, tags |
| Business/application logic | #[Service] | description, tags |
| Database / persistence access | #[Repository] | description, tags |
| Single-use case / interactor | #[Action] | description, tags |
| HTTP controller | #[Controller] | description, tags |
| Domain event (something that happened) | #[Event] | description, tags |
| Event listener / handler | #[Listener] | description, tags |
| HTTP middleware | #[Middleware] | description, tags, priority: int = 0 |
| Queue background job | #[Job] | description, tags, queue: string = 'default' |
| Console / CLI command | #[Command] | description, tags |
| Read-only query (CQRS) | #[Query] | description, tags |
| DDD aggregate root | #[Aggregate] | description, tags |
| DDD value object | #[ValueObject] | description, tags |
Rules:
description should be one clear sentence describing what this class does.tags should be lowercase strings: domain area first (orders, billing), then cross-cutting concern (api, email, ddd, cqrs).#[Middleware], set priority if order matters (lower = runs earlier).#[Job], set queue to the actual queue name if it differs from 'default'.Apply these after the type attribute, in this order if multiple apply:
#[DependsOn] (repeatable, class-level)Add one per injected/required dependency that is meaningful to document.
class: must be the FQCN (use ClassName::class syntax).description: optional — add when the relationship isn't obvious.#[DependsOn(class: PaymentGateway::class, description: 'Primary charge provider')]
#[DependsOn(class: OrderRepository::class)]
#[ListensTo] (repeatable, class-level)Required on any class using #[Listener]. One per event handled.
event: the event class name (short name, not FQCN).description: what this listener does in response.tags: cross-cutting concerns (email, sms, cache).#[ListensTo(event: 'OrderPlaced', description: 'Sends confirmation email', tags: ['email'])]
#[Deprecated] (class or method)Only when the class or method is genuinely deprecated.
reason: required — what to use instead.since: optional semver string.#[Internal] (class)Only for truly internal bootstrap/infrastructure classes not meant as public API.
Work through each public method. Apply attributes in this order:
#[Route] (method, singular)For controller methods and any method that maps to an HTTP endpoint.
method: lowercase verb: 'get', 'post', 'put', 'patch', 'delete'.path: the URI pattern, e.g. '/api/orders/{id}'.description: one sentence — what the endpoint does.#[Authorize] (method, repeatable)When the method checks a permission/policy before proceeding.
ability: dot-notation string, e.g. 'orders.create'.description: optional — add when the rule is non-obvious.#[Validates] (method, repeatable)One per validated field. Read the FormRequest or validation array.
field: exact field name including dot-notation for nested ('items.*.product_id').rules: pipe-separated Laravel validation string.description: optional — add for domain-specific fields.#[Cached] (method, singular)When the method result is cached.
ttl: seconds (default 3600).key: cache key pattern with placeholders, e.g. 'orders.user.{userId}'.description: optional.#[Emits] (method, repeatable)One per domain event dispatched by the method.
event: event class short name.description: when it fires (e.g. 'After the order is persisted').tags: domain area tags.#[SideEffect] (method, repeatable)Observable effects beyond the return value — emails, queue pushes, cache invalidation, external API calls.
description: plain English sentence.tags: cross-cutting concern tags (email, sms, queue, cache, inventory).#[Throws] (method, repeatable)One per exception the method may throw that the caller should handle.
exception: exception class short name or FQCN.description: when it's thrown.email, cache, queue, sms, ddd, cqrs, api).use statements. Only import the attributes you actually apply.Return the complete PHP file with:
use statements grouped after the existing use block, alphabetically sorted.If the user provided a file path, output the annotated version of that file. If they described a class to generate, write the full PHP file from scratch with appropriate stub bodies.
Input — unannotated:
<?php
declare(strict_types=1);
namespace App\Services;
use App\Events\OrderPlaced;
use App\Exceptions\InsufficientStockException;
use App\Exceptions\PaymentFailedException;
use App\Models\Order;
use App\Repositories\OrderRepository;
use App\Payment\PaymentGateway;
final class OrderService
{
public function __construct(
private readonly OrderRepository $orders,
private readonly PaymentGateway $payments,
) {}
public function all(): array
{
return cache()->remember('orders.all', 300, fn () => $this->orders->all());
}
public function create(array $data): Order
{
$order = $this->orders->create($data);
$this->payments->charge($order);
event(new OrderPlaced($order));
return $order;
}
public function cancel(Order $order): void
{
if ($order->isShipped()) {
throw new \RuntimeException('Cannot cancel a shipped order.');
}
$this->orders->delete($order);
}
}
Output — annotated:
<?php
declare(strict_types=1);
namespace App\Services;
use App\Events\OrderPlaced;
use App\Exceptions\InsufficientStockException;
use App\Exceptions\PaymentFailedException;
use App\Models\Order;
use App\Payment\PaymentGateway;
use App\Repositories\OrderRepository;
use JustSteveKing\Signal\Attributes\Cached;
use JustSteveKing\Signal\Attributes\DependsOn;
use JustSteveKing\Signal\Attributes\Emits;
use JustSteveKing\Signal\Attributes\Service;
use JustSteveKing\Signal\Attributes\SideEffect;
use JustSteveKing\Signal\Attributes\Throws;
#[Service(
description: 'Manages order creation, retrieval, and cancellation.',
tags: ['orders'],
)]
#[DependsOn(class: OrderRepository::class)]
#[DependsOn(class: PaymentGateway::class, description: 'Charges the customer on order creation.')]
final class OrderService
{
public function __construct(
private readonly OrderRepository $orders,
private readonly PaymentGateway $payments,
) {}
#[Cached(ttl: 300, key: 'orders.all', description: 'Full order list cached for 5 minutes.')]
public function all(): array
{
return cache()->remember('orders.all', 300, fn () => $this->orders->all());
}
#[Emits(event: 'OrderPlaced', description: 'Fired after the order is persisted and charged.')]
#[SideEffect(description: 'Charges the customer via the payment gateway.', tags: ['payments'])]
#[Throws(exception: PaymentFailedException::class, description: 'When the payment gateway rejects the charge.')]
#[Throws(exception: InsufficientStockException::class, description: 'When a line item cannot be reserved.')]
public function create(array $data): Order
{
$order = $this->orders->create($data);
$this->payments->charge($order);
event(new OrderPlaced($order));
return $order;
}
#[Throws(exception: \RuntimeException::class, description: 'When the order has already shipped.')]
public function cancel(Order $order): void
{
if ($order->isShipped()) {
throw new \RuntimeException('Cannot cancel a shipped order.');
}
$this->orders->delete($order);
}
}
The user may provide:
Use all provided context to write accurate, specific attribute values rather than generic placeholders.
npx claudepluginhub juststeveking/signalAnnotates code using @ai- prefixed markers for technical debt, security vulnerabilities, performance bottlenecks, and accessibility issues to enable AI-assisted workflows.
Guides use of modern PHP features like typed properties, union types, match expressions, named arguments, attributes, enums, and readonly properties for type-safe, expressive code in PHP 7.4+.
Builds PHP 8.3+ applications with Laravel/Symfony, strict typing, PHPStan level 9, and PSR standards. Creates typed DTOs, controllers, migrations, tests, and REST/GraphQL APIs.