acc-create-responder

Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.

16 stars

Best use case

acc-create-responder is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.

Teams using acc-create-responder should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/acc-create-responder/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/acc-create-responder/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/acc-create-responder/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How acc-create-responder Compares

Feature / Agentacc-create-responderStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Responder Generator

Generate ADR-compliant Responder classes for HTTP response building.

## Responder Characteristics

- **Response Building**: Creates complete HTTP Response (status, headers, body)
- **No Business Logic**: Only format and transform data
- **No Domain Access**: No repository or service calls
- **Error Mapping**: Maps domain errors to HTTP status codes
- **Content Type**: Sets appropriate Content-Type header
- **PSR Compliance**: Uses PSR-7 and PSR-17 interfaces

## Template

```php
<?php

declare(strict_types=1);

namespace Presentation\Api\{Context}\{Action};

use Application\{Context}\UseCase\{Action}\{Action}Result;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

final readonly class {Action}Responder
{
    public function __construct(
        private ResponseFactoryInterface $responseFactory,
        private StreamFactoryInterface $streamFactory,
    ) {
    }

    public function respond({Action}Result $result): ResponseInterface
    {
        if ($result->isFailure()) {
            return $this->handleFailure($result);
        }

        return $this->success($result);
    }

    private function success({Action}Result $result): ResponseInterface
    {
        {successResponse}
    }

    private function handleFailure({Action}Result $result): ResponseInterface
    {
        return match ($result->failureReason()) {
            {errorMapping}
            default => $this->badRequest($result->errorMessage()),
        };
    }

    private function json(array $data, int $status = 200): ResponseInterface
    {
        $body = $this->streamFactory->createStream(
            json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)
        );

        return $this->responseFactory->createResponse($status)
            ->withHeader('Content-Type', 'application/json; charset=utf-8')
            ->withBody($body);
    }

    {helperMethods}
}
```

## Test Template

```php
<?php

declare(strict_types=1);

namespace Tests\Unit\Presentation\Api\{Context}\{Action};

use Application\{Context}\UseCase\{Action}\{Action}Result;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use Presentation\Api\{Context}\{Action}\{Action}Responder;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

#[Group('unit')]
#[CoversClass({Action}Responder::class)]
final class {Action}ResponderTest extends TestCase
{
    private ResponseFactoryInterface $responseFactory;
    private StreamFactoryInterface $streamFactory;
    private {Action}Responder $responder;

    protected function setUp(): void
    {
        $this->responseFactory = $this->createMock(ResponseFactoryInterface::class);
        $this->streamFactory = $this->createMock(StreamFactoryInterface::class);
        $this->responder = new {Action}Responder(
            $this->responseFactory,
            $this->streamFactory,
        );

        $this->setupMocks();
    }

    public function testSuccessReturns{ExpectedStatus}(): void
    {
        $result = {Action}Result::success({successData});

        $response = $this->responder->respond($result);

        self::assertSame({expectedStatusCode}, $response->getStatusCode());
    }

    {failureTests}

    private function setupMocks(): void
    {
        $stream = $this->createMock(StreamInterface::class);
        $this->streamFactory->method('createStream')->willReturn($stream);

        $response = $this->createMock(ResponseInterface::class);
        $response->method('withHeader')->willReturnSelf();
        $response->method('withBody')->willReturnSelf();
        $response->method('getStatusCode')->willReturnCallback(
            fn () => $this->responseFactory->lastStatus ?? 200
        );

        $this->responseFactory->method('createResponse')->willReturnCallback(
            function (int $status) use ($response) {
                $this->responseFactory->lastStatus = $status;
                $mock = clone $response;
                $mock->method('getStatusCode')->willReturn($status);
                return $mock;
            }
        );
    }
}
```

## Responder Patterns

### Create Responder (201)

```php
<?php

declare(strict_types=1);

namespace Presentation\Api\User\Create;

use Application\User\UseCase\CreateUser\CreateUserResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

final readonly class CreateUserResponder
{
    public function __construct(
        private ResponseFactoryInterface $responseFactory,
        private StreamFactoryInterface $streamFactory,
    ) {
    }

    public function respond(CreateUserResult $result): ResponseInterface
    {
        if ($result->isFailure()) {
            return match ($result->failureReason()) {
                'email_exists' => $this->conflict('User with this email already exists'),
                'invalid_email' => $this->badRequest('Invalid email format'),
                default => $this->badRequest($result->errorMessage()),
            };
        }

        return $this->created([
            'id' => $result->userId(),
            'email' => $result->email(),
        ]);
    }

    private function created(array $data): ResponseInterface
    {
        return $this->json($data, 201);
    }

    private function conflict(string $message): ResponseInterface
    {
        return $this->json(['error' => $message], 409);
    }

    private function badRequest(string $message): ResponseInterface
    {
        return $this->json(['error' => $message], 400);
    }

    private function json(array $data, int $status): ResponseInterface
    {
        $body = $this->streamFactory->createStream(
            json_encode($data, JSON_THROW_ON_ERROR)
        );

        return $this->responseFactory->createResponse($status)
            ->withHeader('Content-Type', 'application/json')
            ->withBody($body);
    }
}
```

### Get Responder (200/404)

```php
<?php

declare(strict_types=1);

namespace Presentation\Api\User\GetById;

use Application\User\UseCase\GetUserById\GetUserByIdResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

final readonly class GetUserByIdResponder
{
    public function __construct(
        private ResponseFactoryInterface $responseFactory,
        private StreamFactoryInterface $streamFactory,
    ) {
    }

    public function respond(GetUserByIdResult $result): ResponseInterface
    {
        if ($result->isNotFound()) {
            return $this->notFound('User not found');
        }

        $user = $result->user();

        return $this->json([
            'id' => $user->id()->toString(),
            'email' => $user->email()->value(),
            'name' => $user->name(),
            'created_at' => $user->createdAt()->format('c'),
        ]);
    }

    private function notFound(string $message): ResponseInterface
    {
        return $this->json(['error' => $message], 404);
    }

    private function json(array $data, int $status = 200): ResponseInterface
    {
        $body = $this->streamFactory->createStream(
            json_encode($data, JSON_THROW_ON_ERROR)
        );

        return $this->responseFactory->createResponse($status)
            ->withHeader('Content-Type', 'application/json')
            ->withBody($body);
    }
}
```

### List Responder with Pagination

```php
<?php

declare(strict_types=1);

namespace Presentation\Api\User\ListAll;

use Application\User\UseCase\ListUsers\ListUsersResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

final readonly class ListUsersResponder
{
    public function __construct(
        private ResponseFactoryInterface $responseFactory,
        private StreamFactoryInterface $streamFactory,
    ) {
    }

    public function respond(ListUsersResult $result): ResponseInterface
    {
        $users = array_map(
            fn ($user) => [
                'id' => $user->id()->toString(),
                'email' => $user->email()->value(),
                'name' => $user->name(),
            ],
            $result->users()
        );

        return $this->json([
            'data' => $users,
            'meta' => [
                'total' => $result->total(),
                'page' => $result->page(),
                'per_page' => $result->perPage(),
                'total_pages' => $result->totalPages(),
            ],
        ]);
    }

    private function json(array $data, int $status = 200): ResponseInterface
    {
        $body = $this->streamFactory->createStream(
            json_encode($data, JSON_THROW_ON_ERROR)
        );

        return $this->responseFactory->createResponse($status)
            ->withHeader('Content-Type', 'application/json')
            ->withBody($body);
    }
}
```

### Delete Responder (204)

```php
<?php

declare(strict_types=1);

namespace Presentation\Api\User\Delete;

use Application\User\UseCase\DeleteUser\DeleteUserResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

final readonly class DeleteUserResponder
{
    public function __construct(
        private ResponseFactoryInterface $responseFactory,
        private StreamFactoryInterface $streamFactory,
    ) {
    }

    public function respond(DeleteUserResult $result): ResponseInterface
    {
        if ($result->isNotFound()) {
            return $this->notFound('User not found');
        }

        if ($result->isFailure()) {
            return $this->badRequest($result->errorMessage());
        }

        return $this->noContent();
    }

    private function noContent(): ResponseInterface
    {
        return $this->responseFactory->createResponse(204);
    }

    private function notFound(string $message): ResponseInterface
    {
        return $this->json(['error' => $message], 404);
    }

    private function badRequest(string $message): ResponseInterface
    {
        return $this->json(['error' => $message], 400);
    }

    private function json(array $data, int $status): ResponseInterface
    {
        $body = $this->streamFactory->createStream(
            json_encode($data, JSON_THROW_ON_ERROR)
        );

        return $this->responseFactory->createResponse($status)
            ->withHeader('Content-Type', 'application/json')
            ->withBody($body);
    }
}
```

## HTTP Status Mapping

| Domain Condition | HTTP Status | Method |
|------------------|-------------|--------|
| Success (create) | 201 | `created()` |
| Success (read) | 200 | `json()` |
| Success (update) | 200 | `json()` |
| Success (delete) | 204 | `noContent()` |
| Not found | 404 | `notFound()` |
| Already exists | 409 | `conflict()` |
| Validation error | 422 | `unprocessableEntity()` |
| Invalid input | 400 | `badRequest()` |
| Unauthorized | 401 | `unauthorized()` |
| Forbidden | 403 | `forbidden()` |

## File Placement

| Component | Path |
|-----------|------|
| Responder | `src/Presentation/Api/{Context}/{Action}/{Action}Responder.php` |
| Interface | `src/Presentation/Shared/Responder/ResponderInterface.php` |
| Abstract | `src/Presentation/Shared/Responder/AbstractJsonResponder.php` |
| Test | `tests/Unit/Presentation/Api/{Context}/{Action}/{Action}ResponderTest.php` |

## Generation Instructions

When asked to create a Responder:

1. **Identify operation type** (create, read, update, delete)
2. **Determine success status** (201, 200, 204)
3. **List possible failures** and their HTTP codes
4. **Define response structure** (what data to return)
5. **Generate Responder class** with proper namespace
6. **Generate test** for each status code path

## Naming Conventions

| HTTP Method | Responder Name | Success Status |
|-------------|----------------|----------------|
| GET (single) | Get{Resource}ByIdResponder | 200 |
| GET (list) | List{Resource}sResponder | 200 |
| POST | Create{Resource}Responder | 201 |
| PUT | Update{Resource}Responder | 200 |
| PATCH | Patch{Resource}Responder | 200 |
| DELETE | Delete{Resource}Responder | 204 |

## References

For detailed patterns and examples:

- `references/templates.md` — Additional Responder templates
- `references/examples.md` — Real-world Responder examples

Related Skills

agent-ops-create-python-project

16
from diegosouzapw/awesome-omni-skill

Create a plan and issues for implementation of a production-ready Python project with proper structure, tooling, and best practices.

acc-create-use-case

16
from diegosouzapw/awesome-omni-skill

Generates Application Use Cases for PHP 8.5. Creates orchestration services that coordinate domain objects, handle transactions, and dispatch events. Includes unit tests.

acc-create-state

16
from diegosouzapw/awesome-omni-skill

Generates State pattern for PHP 8.5. Creates state machines with context, state interface, and concrete states for behavior changes. Includes unit tests.

acc-create-saga-pattern

16
from diegosouzapw/awesome-omni-skill

Generates Saga pattern components for PHP 8.5. Creates Saga interfaces, steps, orchestrator, state management, and compensation logic with unit tests.

acc-create-retry-pattern

16
from diegosouzapw/awesome-omni-skill

Generates Retry pattern for PHP 8.5. Creates resilience component with exponential backoff, jitter, and configurable retry strategies. Includes unit tests.

acc-create-repository

16
from diegosouzapw/awesome-omni-skill

Generates DDD Repository interfaces and implementation stubs for PHP 8.5. Creates domain interfaces in Domain layer, implementation in Infrastructure.

acc-create-rate-limiter

16
from diegosouzapw/awesome-omni-skill

Generates Rate Limiter pattern for PHP 8.5. Creates request throttling with token bucket, sliding window, and fixed window algorithms. Includes unit tests.

acc-create-psr6-cache

16
from diegosouzapw/awesome-omni-skill

Generates PSR-6 Cache implementation for PHP 8.5. Creates CacheItemPoolInterface and CacheItemInterface implementations with TTL handling and deferred saves. Includes unit tests.

acc-create-psr3-logger

16
from diegosouzapw/awesome-omni-skill

Generates PSR-3 Logger implementation for PHP 8.5. Creates LoggerInterface implementations with log levels, context interpolation, and LoggerAwareTrait usage. Includes unit tests.

acc-create-psr20-clock

16
from diegosouzapw/awesome-omni-skill

Generates PSR-20 Clock implementation for PHP 8.5. Creates ClockInterface implementations including SystemClock, FrozenClock, and OffsetClock for time abstraction and testing. Includes unit tests.

acc-create-psr17-http-factory

16
from diegosouzapw/awesome-omni-skill

Generates PSR-17 HTTP Factories implementation for PHP 8.5. Creates RequestFactoryInterface, ResponseFactoryInterface, StreamFactoryInterface, UriFactoryInterface, ServerRequestFactoryInterface, UploadedFileFactoryInterface. Includes unit tests.

acc-create-psr16-simple-cache

16
from diegosouzapw/awesome-omni-skill

Generates PSR-16 Simple Cache implementation for PHP 8.5. Creates CacheInterface with get/set/delete operations and TTL handling. Includes unit tests.