@ds9-solutions/mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/content/agents/code-reviewer.agent.md +52 -22
  2. package/content/agents/db-analyser.agent.md +121 -0
  3. package/content/agents/security-reviewer.agent.md +119 -37
  4. package/content/agents/test-writer.agent.md +84 -35
  5. package/content/skills/dart/SKILL.md +139 -0
  6. package/content/skills/dart/examples/entity.dart +37 -0
  7. package/content/skills/dart/examples/error_handling.dart +36 -0
  8. package/content/skills/dart/examples/order_test.dart +34 -0
  9. package/content/skills/dart/examples/repository.dart +57 -0
  10. package/content/skills/dart/references/checklist.md +12 -0
  11. package/content/skills/dart/references/documentation.md +26 -0
  12. package/content/skills/dart/references/naming.md +27 -0
  13. package/content/skills/dart-frog/SKILL.md +208 -0
  14. package/content/skills/dart-frog/examples/auth_middleware.dart +50 -0
  15. package/content/skills/dart-frog/examples/controller.dart +75 -0
  16. package/content/skills/dart-frog/examples/dto.dart +41 -0
  17. package/content/skills/dart-frog/examples/error_response.dart +35 -0
  18. package/content/skills/dart-frog/examples/exceptions.dart +60 -0
  19. package/content/skills/dart-frog/examples/middleware_composition.dart +34 -0
  20. package/content/skills/dart-frog/examples/model.dart +20 -0
  21. package/content/skills/dart-frog/examples/port.dart +17 -0
  22. package/content/skills/dart-frog/examples/repository.dart +56 -0
  23. package/content/skills/dart-frog/examples/route_collection.dart +25 -0
  24. package/content/skills/dart-frog/examples/route_item.dart +24 -0
  25. package/content/skills/dart-frog/examples/route_test.dart +33 -0
  26. package/content/skills/dart-frog/examples/service.dart +51 -0
  27. package/content/skills/dart-frog/examples/service_test.dart +55 -0
  28. package/content/skills/dart-frog/references/checklist.md +31 -0
  29. package/content/skills/dart-frog/references/error-handling.md +48 -0
  30. package/content/skills/dart-frog/references/fpdart-taskeither.md +53 -0
  31. package/content/skills/dart-frog/references/layers.md +43 -0
  32. package/content/skills/dart-frog/references/middlewares-di.md +45 -0
  33. package/content/skills/dart-frog/references/project-structure.md +67 -0
  34. package/content/skills/dart-frog/references/routing.md +53 -0
  35. package/content/skills/dart-frog/references/testing.md +24 -0
  36. package/content/skills/docker/SKILL.md +3 -3
  37. package/content/skills/driftorm/SKILL.md +249 -0
  38. package/content/skills/flutter/SKILL.md +419 -0
  39. package/content/skills/hex-pattern-dart/SKILL.md +83 -0
  40. package/content/skills/hex-pattern-dart/examples/adapter-in.md +24 -0
  41. package/content/skills/hex-pattern-dart/examples/adapter-out.md +20 -0
  42. package/content/skills/hex-pattern-dart/examples/errors.md +25 -0
  43. package/content/skills/hex-pattern-dart/examples/port.md +11 -0
  44. package/content/skills/hex-pattern-dart/examples/service.md +20 -0
  45. package/content/skills/hex-pattern-dart/examples/structure-tree.md +28 -0
  46. package/content/skills/hex-pattern-dart/references/anti-patterns.md +16 -0
  47. package/content/skills/hex-pattern-dart/references/checklist.md +34 -0
  48. package/content/skills/hex-pattern-dart/references/dependency-injection.md +32 -0
  49. package/content/skills/hex-pattern-dart/references/error-layers.md +27 -0
  50. package/content/skills/hex-pattern-dart/references/layers-and-dependency-rule.md +29 -0
  51. package/content/skills/hex-pattern-dart/references/persistence.md +23 -0
  52. package/content/skills/hex-pattern-dart/references/ports-and-adapters.md +33 -0
  53. package/content/skills/hex-pattern-dart/references/testing.md +30 -0
  54. package/content/skills/mysql/SKILL.md +3 -3
  55. package/content/skills/typescript/SKILL.md +4 -4
  56. package/dist/src/content.js +10 -2
  57. package/dist/src/init.js +1 -0
  58. package/dist/src/search.js +20 -12
  59. package/dist/src/server.js +60 -4
  60. package/dist/src/skill-files.js +125 -0
  61. package/package.json +2 -2
  62. package/content/agents/db-specialist.agent.md +0 -78
  63. package/content/agents/nestjs-api-designer.agent.md +0 -61
  64. package/content/skills/hexagonal/SKILL.md +0 -156
  65. package/content/skills/jest-testing/SKILL.md +0 -88
  66. package/content/skills/nestjs/SKILL.md +0 -100
  67. package/content/skills/typeorm/SKILL.md +0 -119
  68. package/dist/test/mcp.test.js +0 -112
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: dart
3
+ description: Convenções da linguagem Dart da DS9 — orientação a objetos como abordagem padrão, nomenclatura oficial do Dart (Effective Dart), documentação com `///`, null safety, tratamento de erros, análise estática, formatação e testes. Use ao escrever ou revisar qualquer código Dart (CLI, servidor, pacote ou app Flutter), definir classes/tipos, nomear arquivos e identificadores, documentar código, ou quando o usuário pedir "boas práticas Dart", "padrão Dart", "nomenclatura Dart" ou "documentar com ///".
4
+ ---
5
+
6
+ # Dart — Diretrizes DS9
7
+
8
+ Convenções da **linguagem Dart**, válidas para qualquer projeto (CLI, servidor, pacote ou aplicativo). Regras específicas de Flutter (widgets, estado, UI) não fazem parte desta skill. Base: guia oficial *Effective Dart*, documentação do Dart e as skills Dart do projeto `lazy_forge` (`.agents/skills`). Em caso de dúvida sobre versão da linguagem, confirme a versão do SDK no `pubspec.yaml` antes de usar recursos novos.
9
+
10
+ ## 1. Orientação a objetos como padrão
11
+
12
+ O código Dart da DS9 é **orientado a objetos**. Modele o domínio com classes e deixe o comportamento junto dos dados.
13
+
14
+ - **Comportamento em métodos, não em funções soltas:** lógica de negócio vive em classes (entidades, serviços, casos de uso). Funções de nível superior ficam restritas a `main()` e a pequenos utilitários verdadeiramente genéricos e sem estado; mesmo estes, prefira como métodos estáticos de uma classe coesa ou como `extension`.
15
+ - **Encapsulamento:** campos privados (`_campo`) com acesso por getters/métodos; exponha o mínimo necessário. Estado interno nunca é alterado de fora da classe.
16
+ - **Imutabilidade por padrão:** campos `final`, construtores `const` quando possível, e novas instâncias (`copyWith`/métodos que retornam um novo objeto) em vez de mutação onde o domínio pedir.
17
+ - **Composição sobre herança:** injete dependências pelo construtor (como `abstract interface class` ou `abstract class`) e componha objetos; use herança só para relação "é um" genuína.
18
+ - **Polimorfismo no lugar de `if`/`switch` por tipo:** variação de comportamento vai em subclasses/implementações, não em cadeias de `is`/`switch` espalhadas. Use `sealed class` para famílias fechadas de tipos; `switch` exaustivo sobre elas é aceitável quando o comportamento não pertence ao tipo (ex.: decodificar dados externos).
19
+ - **Contratos explícitos:** dependências externas (banco, rede, relógio) atrás de uma interface (`abstract interface class`), para troca e teste.
20
+
21
+ ### Evite o estilo funcional como abordagem principal
22
+
23
+ Dart é multiparadigma, mas aqui o padrão é objetos. Isso **não** proíbe os recursos idiomáticos da linguagem; proíbe construir a arquitetura em torno de funções.
24
+
25
+ - Evite pipelines longos e encadeados (`map().where().fold().expand()...`) que escondem regra de negócio; se passar de 2 ou 3 etapas, extraia para um método nomeado ou use um laço `for` claro.
26
+ - Evite passar funções/closures como mecanismo principal de extensão (callbacks em cascata, funções de ordem superior como "estratégia"); prefira um objeto com método bem nomeado.
27
+ - Evite funções que retornam funções, currying e composição de funções; evite tipos como `Function` solto (use `typedef` ou interface quando um callback for inevitável).
28
+ - Evite `Either`/`Result` genéricos e estilos monádicos criados à mão quando exceção tipada ou um tipo de resultado `sealed` do domínio resolve de forma mais legível. **Exceção:** em projetos que seguem as skills `hex-pattern-dart`, `dart-frog` ou `flutter`, o `TaskEither` do `fpdart` (com `TaskEither.tryCatch`, `map`, `flatMap`, `mapLeft`) é o padrão obrigatório nos contratos entre camadas; ele não se estende ao domínio e aos models, que continuam orientados a objetos.
29
+ - Aceitável e idiomático: `for` com `collection-if`/`collection-for` em literais, `map`/`where`/`toList` simples (1 a 2 etapas), `async`/`await`, callbacks exigidos por APIs de terceiros e a composição de `TaskEither` conforme essas skills.
30
+
31
+ ## 2. Nomenclatura (Effective Dart)
32
+
33
+ | Elemento | Convenção | Exemplo |
34
+ |---|---|---|
35
+ | Classes, enums, mixins, extensions, typedefs, parâmetros de tipo | `UpperCamelCase` | `OrderService`, `PaymentStatus`, `T` |
36
+ | Variáveis, parâmetros, campos, métodos, funções, getters | `lowerCamelCase` | `totalPrice`, `findById()` |
37
+ | **Constantes** (incl. `const`/`static const`/valores de enum) | `lowerCamelCase` (**não** `SCREAMING_CAPS`) | `defaultTimeout`, `maxRetries` |
38
+ | Pacotes, diretórios, arquivos, prefixos de import | `lowercase_with_underscores` | `order_service.dart`, `import '...' as http_client;` |
39
+ | Membros e tipos privados da biblioteca | prefixo `_` | `_cache`, `_OrderMapper` |
40
+
41
+ Regras adicionais:
42
+
43
+ - **Arquivo = classe principal:** `UserRepository` fica em `user_repository.dart`. Uma classe pública principal por arquivo; testes em `<nome>_test.dart`.
44
+ - **Siglas:** com mais de duas letras, capitalize como palavra (`HttpClient`, `JsonParser`, `UserId`); com duas letras, mantenha maiúsculas (`IOSink`).
45
+ - **Sem prefixos/sufixos de tipo:** não use `I` para interfaces (`IUserRepository`), notação húngara, nem `k` em constantes (`kTimeout`).
46
+ - **Booleanos** com nome afirmativo: `isEmpty`, `hasPermission`, `canRetry`; evite negações (`isNotValid`).
47
+ - **Métodos** começam com verbo (`calculateTotal`, `save`); **getters e propriedades** são substantivos, sem prefixo `get` (`total`, não `getTotal`).
48
+ - **Nomes descritivos:** sem abreviações obscuras nem nomes de uma letra fora de laços/lambdas curtos; o nome expressa intenção, não implementação.
49
+ - **Variáveis não usadas:** use `_` (wildcard) em vez de nomes sem uso.
50
+ - **Ordem em arquivos:** `dart:` → `package:` → relativos, cada grupo em ordem alfabética; prefira imports relativos dentro de `lib/` do próprio pacote, exceto quando a skill do framework definir outro padrão (a `flutter` usa `package:`).
51
+
52
+ ## 3. Documentação e comentários (`///`)
53
+
54
+ **Regra de inclusão:** toda declaração **pública** (biblioteca, classe, enum, mixin, extension, construtor nomeado relevante, método, função, getter/setter, campo e constante) **deve** ter comentário de documentação com `///`. Declarações privadas só precisam quando a lógica não for óbvia. Código novo sem `///` nas declarações públicas é pendência de review.
55
+
56
+ - Use **sempre `///`**, em linhas consecutivas; **nunca** `/** ... */`. Use `//` apenas para comentários de implementação internos, explicando o **porquê**, nunca o óbvio.
57
+ - **Primeira linha:** uma frase única e resumida, terminada em ponto, seguida de uma linha `///` vazia antes do restante. Uma linha totalmente em branco (sem `///`) encerra o comentário.
58
+ - **Abertura por tipo de membro:** propriedades/getters com sintagma nominal (`/// O raio da esfera.`); booleanos com "Se"/"Whether" no idioma do projeto (`/// Se a conexão está ativa.`); métodos com verbo na 3ª pessoa (`/// Inicializa o banco.`).
59
+ - **Sem redundância:** não repita a assinatura nem o nome ("Esta classe é...", "O método foo faz...").
60
+ - **Sem tags estilo Javadoc** (`@param`, `@return`, `@throws`): descreva parâmetros, retorno e exceções em prosa, referenciando parâmetros com `[nome]`.
61
+ - **Links:** use colchetes para símbolos no escopo (`[Order]`, `[Order.total]`, sem parênteses); construtor sem nome como `[Order.new]`; crases para palavras-chave e literais (`` `null` ``, `` `true` ``). Blocos de código com a linguagem (` ```dart `).
62
+ - **Posição:** o comentário `///` vem **antes** das anotações (`@override` etc.).
63
+ - **Herança e pares getter/setter:** não duplique documentação em `@override` sem mudança de comportamento; em par getter/setter, documente só o getter.
64
+ - **Idioma:** siga o idioma já usado no projeto, de forma consistente.
65
+ - Valide com `dart analyze` (referências entre colchetes resolvidas) e, opcionalmente, `dart doc`.
66
+
67
+ ```dart
68
+ // Evitar
69
+ /**
70
+ * Busca o pedido.
71
+ * @param id identificador
72
+ * @return o pedido
73
+ */
74
+ Order find(String id) { ... }
75
+
76
+ // Preferir
77
+ /// Busca o pedido pelo [id].
78
+ ///
79
+ /// Lança [OrderNotFoundException] se não existir pedido com esse identificador.
80
+ Order findById(String id) { ... }
81
+ ```
82
+
83
+ ## 4. Construtores e classes
84
+
85
+ - Prefira **parâmetros iniciais** (`this.campo`) e `super.parametro` a atribuições manuais; use `required` em parâmetros nomeados obrigatórios.
86
+ - Construtores `const` em classes imutáveis; `factory` para validação/cache e para criar a partir de dados externos (`fromJson`).
87
+ - **Invariantes no construtor/factory:** estado inválido não pode existir; valide e lance exceção de domínio.
88
+ - **Primary constructors** (declarar campos no cabeçalho da classe): disponíveis só a partir do Dart 3.13 (experimental no 3.12, via flag). Use **somente se o SDK do projeto suportar**; caso contrário, use construtores tradicionais. Não migre código existente apenas por isso.
89
+ - Use `sealed`, `final`, `base` e `interface` para controlar quem pode estender ou implementar uma classe, quando a API for pública.
90
+
91
+ ## 5. Null safety e tipos
92
+
93
+ - Nada de `dynamic` em código novo; tipos explícitos em APIs públicas, em genéricos (`List<Order>`) e em retornos.
94
+ - Evite `!` (asserção de nulo); prefira promoção de tipo, `?.`, `??` ou tratar o nulo. `late` só quando a inicialização estiver garantida antes do uso.
95
+ - Prefira `final` a `var` quando o valor não muda; evite `as` sem garantia do tipo em tempo de execução.
96
+ - **Pattern matching e records** (Dart 3): use para decodificar dados externos (JSON) e `switch` exaustivo em `sealed`; não use para o que um `is` simples, um acesso a propriedade ou uma comparação booleana resolvem. Dados malformados devem falhar com erro explícito (`FormatException`), nunca ser descartados em silêncio.
97
+
98
+ ## 6. Tratamento de erros
99
+
100
+ - Falhas recuperáveis: **`Exception` tipada do domínio** (`class OrderNotFoundException implements Exception`), nunca `throw 'texto'` nem lançar `Exception` genérica. Em projetos `hex-pattern-dart`/`dart-frog`, as falhas trafegam como `Left` de `TaskEither` com as exceções por camada definidas em `hex-pattern-dart` (em Flutter, siga a skill `flutter`) (`BusinessException`, `ClientException`, `MiddlewareException`).
101
+ - **Nunca capture `Error`** (nem `TypeError`, `ArgumentError`, etc.): indicam bugs de programação. Capture tipos específicos; não use `catch (e)` amplo sem relançar.
102
+ - Use `rethrow` para propagar preservando o stack trace; não engula exceções em silêncio nem registre dados sensíveis.
103
+ - Mensagens de erro expostas a usuários não vazam detalhes internos.
104
+
105
+ ## 7. Assincronismo
106
+
107
+ - `async`/`await` em vez de encadear `.then()`; sempre aguarde (`await`) ou trate explicitamente os `Future`s (sem "fire-and-forget" acidental).
108
+ - Libere recursos (`close`/`cancel`/`dispose`) em `try/finally`; cancele subscriptions e controllers de `Stream`.
109
+ - Não bloqueie o isolate principal com trabalho pesado síncrono; use isolates quando necessário.
110
+
111
+ ## 8. Qualidade, formatação e testes
112
+
113
+ - **Análise estática:** `analysis_options.yaml` na raiz do pacote com um conjunto base (`package:lints/recommended.yaml` ou `package:flutter_lints/flutter.yaml`) e `strict-casts`, `strict-inference` e `strict-raw-types` ativos. Exclua só código gerado (`**/*.g.dart`).
114
+ - Rode `dart analyze` e corrija os avisos; use `dart fix --apply` para correções mecânicas (revise com `--dry-run`) e `dart format .` antes de commitar. Suprima diagnósticos (`// ignore:`) só com justificativa e no menor escopo.
115
+ - **Testes:** `package:test` (ou `flutter_test` em Flutter), arquivos em `test/` espelhando `lib/` com sufixo `_test.dart`; `group()` por funcionalidade, `setUp()`/`tearDown()` para preparo, `expect()` com matchers; dependências externas isoladas por interface/mocks. Para criar e executar testes, use o agente `test-writer`.
116
+
117
+ ## 9. Checklist de review
118
+
119
+ - [ ] Regra de negócio está em classes, sem funções soltas nem cadeias funcionais longas?
120
+ - [ ] Toda declaração pública tem `///` no formato da seção 3?
121
+ - [ ] Nomes seguem a tabela da seção 2 (inclusive constantes em `lowerCamelCase` e arquivos em `snake_case`)?
122
+ - [ ] Sem `dynamic`, `!` desnecessário ou `catch` de `Error`?
123
+ - [ ] Campos `final`, encapsulamento e construtores `const` onde cabem?
124
+ - [ ] `dart analyze` limpo, `dart format` aplicado e testes cobrindo o comportamento novo?
125
+
126
+ ## 10. Arquivos complementares
127
+
128
+ Em caso de dúvida, consulte (via `list_skill_files` / `get_skill_file` com `skill="dart"`; ou direto na pasta `dart/`, se o host não tiver as tools):
129
+
130
+ - `examples/` — código de referência: `entity.dart`, `repository.dart`, `error_handling.dart`, `order_test.dart`. Use quando precisar de um exemplo de entidade, contrato/caso de uso, erros ou teste.
131
+ - `references/` — `naming.md` (nomenclatura), `documentation.md` (`///`), `checklist.md` (review detalhado).
132
+
133
+ ## Não fazer
134
+
135
+ - Não usar `/** ... */` nem tags `@param`/`@return`/`@throws`.
136
+ - Não usar `SCREAMING_CAPS`, prefixo `I`, `k` ou notação húngara.
137
+ - Não criar funções de nível superior com regra de negócio nem pipelines funcionais extensos (exceto a composição de `TaskEither` exigida pelas skills `hex-pattern-dart`, `dart-frog` e `flutter`).
138
+ - Não declarar `dynamic`, `Function` solto ou `throw` de strings.
139
+ - Não duplicar regras de outras skills; para testes, ver o agente `test-writer`.
@@ -0,0 +1,37 @@
1
+ /// Exemplo: entidade imutável, encapsulada e documentada com `///`.
2
+ library;
3
+
4
+ /// Status possíveis de um [Order].
5
+ enum OrderStatus {
6
+ /// Pedido criado e ainda não pago.
7
+ pending,
8
+
9
+ /// Pedido pago.
10
+ paid,
11
+ }
12
+
13
+ /// Pedido do domínio, com invariantes validadas na criação.
14
+ class Order {
15
+ /// Cria um pedido.
16
+ ///
17
+ /// Lança [ArgumentError] se [id] estiver vazio ou [total] for negativo.
18
+ Order({required this.id, required this.total, this.status = OrderStatus.pending}) {
19
+ if (id.isEmpty) throw ArgumentError.value(id, 'id', 'não pode ser vazio');
20
+ if (total < 0) throw ArgumentError.value(total, 'total', 'não pode ser negativo');
21
+ }
22
+
23
+ /// Identificador único do pedido.
24
+ final String id;
25
+
26
+ /// Valor total do pedido.
27
+ final double total;
28
+
29
+ /// Situação atual do pedido.
30
+ final OrderStatus status;
31
+
32
+ /// Se o pedido já foi pago.
33
+ bool get isPaid => status == OrderStatus.paid;
34
+
35
+ /// Retorna uma cópia deste pedido já marcada como paga.
36
+ Order markAsPaid() => Order(id: id, total: total, status: OrderStatus.paid);
37
+ }
@@ -0,0 +1,36 @@
1
+ /// Exemplo: exceções tipadas e tratamento sem engolir erros.
2
+ library;
3
+
4
+ /// Falha ao decodificar dados externos.
5
+ class InvalidPayloadException implements Exception {
6
+ /// Cria a exceção com a [message] explicando o problema.
7
+ const InvalidPayloadException(this.message);
8
+
9
+ /// Descrição do problema, segura para exibir.
10
+ final String message;
11
+
12
+ @override
13
+ String toString() => 'InvalidPayloadException: $message';
14
+ }
15
+
16
+ /// Lê um valor numérico de um payload externo.
17
+ class PayloadReader {
18
+ /// Lê o campo `amount` de [json].
19
+ ///
20
+ /// Lança [InvalidPayloadException] se ele não existir ou não for numérico.
21
+ double readAmount(Map<String, Object?> json) {
22
+ return switch (json['amount']) {
23
+ final num amount => amount.toDouble(),
24
+ _ => throw const InvalidPayloadException('campo "amount" ausente ou inválido'),
25
+ };
26
+ }
27
+
28
+ /// Lê o valor e propaga falhas de domínio preservando o stack trace.
29
+ double readOrRethrow(Map<String, Object?> json) {
30
+ try {
31
+ return readAmount(json);
32
+ } on InvalidPayloadException {
33
+ rethrow; // nunca `catch (e)` amplo nem captura de `Error`
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,34 @@
1
+ /// Exemplo: teste unitário com `group`, `setUp` e `expect`.
2
+ ///
3
+ /// Em um projeto real o arquivo fica em `test/`, espelhando `lib/`.
4
+ library;
5
+
6
+ import 'package:test/test.dart';
7
+
8
+ import 'entity.dart';
9
+ import 'repository.dart';
10
+
11
+ void main() {
12
+ group('PayOrder', () {
13
+ late InMemoryOrderRepository repository;
14
+ late PayOrder useCase;
15
+
16
+ setUp(() {
17
+ repository = InMemoryOrderRepository();
18
+ useCase = PayOrder(repository);
19
+ });
20
+
21
+ test('marca o pedido como pago e persiste', () async {
22
+ await repository.save(Order(id: '1', total: 10));
23
+
24
+ final paid = await useCase.execute('1');
25
+
26
+ expect(paid.isPaid, isTrue);
27
+ expect((await repository.findById('1'))!.isPaid, isTrue);
28
+ });
29
+
30
+ test('lança OrderNotFoundException quando o pedido não existe', () {
31
+ expect(() => useCase.execute('x'), throwsA(isA<OrderNotFoundException>()));
32
+ });
33
+ });
34
+ }
@@ -0,0 +1,57 @@
1
+ /// Exemplo: contrato (porta) e implementação, com dependência injetada.
2
+ library;
3
+
4
+ import 'entity.dart';
5
+
6
+ /// Contrato de persistência de pedidos.
7
+ abstract interface class OrderRepository {
8
+ /// Busca o pedido pelo [id]; retorna `null` se não existir.
9
+ Future<Order?> findById(String id);
10
+
11
+ /// Salva o [order].
12
+ Future<void> save(Order order);
13
+ }
14
+
15
+ /// Implementação em memória, útil em testes.
16
+ class InMemoryOrderRepository implements OrderRepository {
17
+ final Map<String, Order> _orders = {};
18
+
19
+ @override
20
+ Future<Order?> findById(String id) async => _orders[id];
21
+
22
+ @override
23
+ Future<void> save(Order order) async {
24
+ _orders[order.id] = order;
25
+ }
26
+ }
27
+
28
+ /// Caso de uso: paga um pedido existente.
29
+ class PayOrder {
30
+ /// Cria o caso de uso com o [repository] injetado.
31
+ PayOrder(this._repository);
32
+
33
+ final OrderRepository _repository;
34
+
35
+ /// Marca como pago o pedido [id].
36
+ ///
37
+ /// Lança [OrderNotFoundException] se o pedido não existir.
38
+ Future<Order> execute(String id) async {
39
+ final order = await _repository.findById(id);
40
+ if (order == null) throw OrderNotFoundException(id);
41
+ final paid = order.markAsPaid();
42
+ await _repository.save(paid);
43
+ return paid;
44
+ }
45
+ }
46
+
47
+ /// Erro de domínio: pedido não encontrado.
48
+ class OrderNotFoundException implements Exception {
49
+ /// Cria a exceção para o pedido [id].
50
+ const OrderNotFoundException(this.id);
51
+
52
+ /// Identificador procurado.
53
+ final String id;
54
+
55
+ @override
56
+ String toString() => 'Pedido não encontrado: $id';
57
+ }
@@ -0,0 +1,12 @@
1
+ # Dart — Checklist de review detalhado
2
+
3
+ - [ ] Regra de negócio em classes; sem funções soltas nem pipelines longos.
4
+ - [ ] Dependências externas atrás de `abstract interface class`, injetadas no construtor.
5
+ - [ ] Campos `final`, construtores `const` onde couber, estado privado encapsulado.
6
+ - [ ] Toda declaração pública com `///` (sem `/** */`, sem `@param`).
7
+ - [ ] Nomes: `UpperCamelCase` (tipos), `lowerCamelCase` (membros e constantes), `snake_case` (arquivos).
8
+ - [ ] Sem `dynamic`, `!` desnecessário, `Function` solto ou `throw` de string.
9
+ - [ ] Sem `catch` de `Error`; `rethrow` preserva o stack trace.
10
+ - [ ] `Future`s aguardados; recursos liberados em `try/finally`.
11
+ - [ ] `dart analyze` limpo, `dart format` aplicado.
12
+ - [ ] Testes em `test/` espelhando `lib/`, com `group`/`setUp`/`expect`.
@@ -0,0 +1,26 @@
1
+ # Dart — Documentação com `///` (referência detalhada)
2
+
3
+ Complementa a seção 3 do `SKILL.md`.
4
+
5
+ ## Modelo
6
+
7
+ ```dart
8
+ /// Calcula o total do pedido.
9
+ ///
10
+ /// Soma os itens de [items] e aplica [discount] (entre 0 e 1).
11
+ /// Lança [ArgumentError] se [discount] estiver fora do intervalo.
12
+ double calculateTotal(List<Item> items, {double discount = 0}) { ... }
13
+ ```
14
+
15
+ ## Regras rápidas
16
+
17
+ - Primeira linha: frase única, terminada em ponto; depois linha `///` vazia.
18
+ - Propriedade: sintagma nominal. Booleano: "Se ...". Método: verbo na 3ª pessoa.
19
+ - Sem `@param`/`@return`; use prosa e `[nome]`.
20
+ - `///` antes de `@override` e outras anotações.
21
+ - Par getter/setter: documente só o getter.
22
+ - Não documente `@override` sem mudança de comportamento.
23
+
24
+ ## Validação
25
+
26
+ `dart analyze` resolve referências entre colchetes; `dart doc` gera a documentação.
@@ -0,0 +1,27 @@
1
+ # Dart — Nomenclatura (referência detalhada)
2
+
3
+ Complementa a seção 2 do `SKILL.md`.
4
+
5
+ ## Certo x errado
6
+
7
+ | Evitar | Preferir | Motivo |
8
+ |---|---|---|
9
+ | `MAX_RETRIES`, `kMaxRetries` | `maxRetries` | Constantes usam `lowerCamelCase` |
10
+ | `IUserRepository` | `UserRepository` | Sem prefixo `I` |
11
+ | `UserRepository.dart` | `user_repository.dart` | Arquivos em `snake_case` |
12
+ | `getTotal()` | `total` (getter) | Propriedade é substantivo, sem `get` |
13
+ | `isNotValid` | `isValid` | Booleano afirmativo |
14
+ | `HTTPClient` | `HttpClient` | Siglas com mais de 2 letras viram palavra |
15
+ | `data`, `tmp`, `x` | `orderTotals` | Nome expressa intenção |
16
+
17
+ ## Ordem de imports
18
+
19
+ 1. `dart:`
20
+ 2. `package:`
21
+ 3. relativos
22
+
23
+ Cada grupo em ordem alfabética, separado por linha em branco.
24
+
25
+ ## Privacidade
26
+
27
+ O prefixo `_` torna o membro privado à **biblioteca** (arquivo), não à classe.
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: dart-frog
3
+ description: Diretrizes DS9 para backends com Dart Frog em arquitetura hexagonal (domain/adapter por contexto) — rotas finas com 405 obrigatório, injeção de dependência exclusivamente por middleware, `fpdart` com `TaskEither`/`TaskEither.tryCatch` como retorno entre camadas e exceções por camada (BusinessException, ClientException, MiddlewareException). Persistência é agnóstica (use a skill de persistência do projeto). Use ao criar, editar, revisar ou testar rotas, `_middleware.dart`, controllers, services, ports, repositories ou DTOs em um projeto Dart Frog (pasta `routes/`, comandos `dart_frog dev/build`), ou quando o usuário pedir "backend Dart", "API Dart Frog" ou "arquitetura hexagonal em Dart".
4
+ ---
5
+
6
+ # Dart Frog — Diretrizes DS9
7
+
8
+ Padrão DS9 para APIs em **Dart Frog** com **arquitetura hexagonal** (ports & adapters), organizada por contexto de negócio. Complementa as skills `dart` (nomenclatura, `///`, OOP, null safety) e `hex-pattern-dart` (camadas, ports/adapters, erros): siga as três. A persistência é **agnóstica** aqui; para o banco/ORM do projeto, consulte a skill de persistência correspondente (`list_skills` / `recommend_for_task`).
9
+
10
+ ## 0. Pré-requisitos (verifique antes de gerar código)
11
+
12
+ 1. Confirme no `pubspec.yaml` do projeto as dependências `dart_frog` e **`fpdart`**.
13
+ 2. **Se `fpdart` não estiver instalado, informe ao usuário que ele é obrigatório para seguir o padrão DS9** e peça autorização para instalar (`dart pub add fpdart`). Não gere código fora do padrão para contornar a ausência.
14
+ 3. Use as versões já fixadas no `pubspec.yaml`; não atualize dependências sem pedido explícito.
15
+ 4. Preserve a estrutura existente em projetos pré-existentes; aplique o padrão integralmente em código novo e no que for explicitamente solicitado, sem refatorar o restante.
16
+
17
+ ## 1. Regras inegociáveis
18
+
19
+ 1. **`fpdart` + `TaskEither`:** todo contrato de camada (port, service, repository, controller) que pode falhar **retorna `TaskEither<Falha, T>`**, e a operação assíncrona é envolvida com **`TaskEither.tryCatch`**. Nunca lance exceção para fora de uma camada nem converta falha em sucesso vazio.
20
+ 2. **Injeção só por middleware:** dependências são compostas em `_middleware.dart` com `provider<T>` e lidas com `context.read<T>()`. Nada de instanciar service/repository/controller no handler, sem service locator, sem singleton global fora do middleware.
21
+ 3. **405 sempre:** todo handler faz `switch` em `context.request.method` e termina com `case _: return Response(statusCode: HttpStatus.methodNotAllowed);`.
22
+ 4. **Camadas de erro fixas:** `BusinessException` (service/repository), `ClientException` (controller), `MiddlewareException` (middleware). Nomes e responsabilidades não mudam entre projetos.
23
+ 5. **Persistência agnóstica:** o domínio só conhece **ports**; o acesso a dados fica em `adapter/repository`, sem citar ORM/driver nesta skill.
24
+
25
+ ## 2. Estrutura
26
+
27
+ ```text
28
+ <projeto>/
29
+ ├── routes/ # roteador (arquivos = rotas); só despacho HTTP
30
+ │ ├── _middleware.dart # raiz: CORS, infraestrutura compartilhada
31
+ │ ├── api/_middleware.dart # autenticação do grupo
32
+ │ └── api/orders/
33
+ │ ├── _middleware.dart # composição do contexto (providers)
34
+ │ ├── index.dart # /api/orders
35
+ │ └── [id].dart # /api/orders/:id
36
+ ├── lib/
37
+ │ ├── <contexto>/
38
+ │ │ ├── domain/{models,ports,services}/
39
+ │ │ └── adapter/{controllers,dtos,repository}/
40
+ │ └── common/{exception,http,middleware,environment,logger,models}/
41
+ ├── test/ # espelha lib/ e routes/
42
+ └── main.dart # só init() e run()
43
+ ```
44
+
45
+ Estrutura completa e variações (`application/use_case`, adapters de integração): `references/project-structure.md`.
46
+
47
+ ## 3. Camadas e regra de dependência
48
+
49
+ | Camada | Responsabilidade | Retorno | Falha |
50
+ |---|---|---|---|
51
+ | `routes/**` | despachar método HTTP, ler `controller` do contexto, converter resultado em `Response` | `Future<Response>` | converte `Left` em JSON de erro |
52
+ | `adapter/controllers` | ler body/query/path, validar formato (DTO), chamar service, converter modelo → DTO | `TaskEither<ClientException, T>` | `ClientException` |
53
+ | `domain/services` | regras de negócio, autorização de domínio, composição de ports | `TaskEither<BusinessException, T>` | `BusinessException` |
54
+ | `domain/ports` | contratos (`abstract interface class`) | `TaskEither<BusinessException, T>` | `BusinessException` |
55
+ | `adapter/repository` | implementa o port e acessa a persistência | `TaskEither<BusinessException, T>` | `BusinessException` |
56
+ | `middlewares` | autenticar, compor providers, encerrar com erro | `Handler` | `MiddlewareException` |
57
+
58
+ Dependências apontam para dentro: `routes → controller → service → port ← repository`.
59
+
60
+ - `domain/` **nunca** importa `dart_frog`, SDK, driver/ORM, `adapter/`, `RequestContext` nem `Response`. Pode importar `fpdart`, `equatable`, `json_annotation` e `common/exception`.
61
+ - Contextos se comunicam por **port ou service público**, nunca pelo repository concreto de outro contexto, sem dependência circular.
62
+ - Mapeamento: linha de banco → modelo no repository; modelo → DTO no controller/DTO. Modelos de domínio não conhecem DTOs nem classes geradas.
63
+
64
+ Detalhes e responsabilidades por camada: `references/layers.md`.
65
+
66
+ ## 4. `TaskEither` e `tryCatch`
67
+
68
+ ```dart
69
+ // repository: toda chamada que pode falhar entra em tryCatch
70
+ TaskEither<BusinessException, Order?> findById(String id) {
71
+ return TaskEither.tryCatch(
72
+ () => _dataSource.findById(id),
73
+ (error, stackTrace) => BusinessException(
74
+ domainName: 'order',
75
+ statusCode: HttpStatus.internalServerError,
76
+ message: 'Falha ao consultar o pedido.',
77
+ error: error,
78
+ stackTrace: stackTrace,
79
+ ),
80
+ );
81
+ }
82
+
83
+ // service: compõe com flatMap/map/mapLeft; regra inválida vira TaskEither.left
84
+ TaskEither<BusinessException, Order> getById(String id) =>
85
+ _repository.findById(id).flatMap(
86
+ (order) => order == null
87
+ ? TaskEither.left(_notFound(id))
88
+ : TaskEither.right(order),
89
+ );
90
+ ```
91
+
92
+ - Prefira `TaskEither.tryCatch`, `map`, `flatMap`, `mapLeft`; execute (`.run()`) **só na rota**.
93
+ - Não use `Exception` genérica como falha; não capture `Error`; não use `catch (_)` amplo.
94
+ - Encadeamentos de `TaskEither` são a exceção funcional aceita da skill `dart`; fora desse contrato, continue em OOP.
95
+
96
+ Guia completo: `references/fpdart-taskeither.md`.
97
+
98
+ ## 5. Erros por camada
99
+
100
+ | Tipo | Camada | Campos |
101
+ |---|---|---|
102
+ | `BusinessException` | service, repository | `domainName`, `statusCode`, `message` (seguro para o cliente), `error` (causa original, nunca exposta), `stackTrace` |
103
+ | `ClientException` | controller | `statusCode`, `message` |
104
+ | `MiddlewareException` | middleware | `statusCode`, `message`, `stackTrace?` |
105
+
106
+ - O controller converte `BusinessException` em `ClientException` preservando o `statusCode`; para `5xx`, use mensagem genérica.
107
+ - Formato único de erro: `{"error": {"code": "...", "message": "..."}}`.
108
+ - **Nunca** envie stack trace, SQL, causa original (`error`), caminho de arquivo ou detalhe de SDK ao cliente; registre no log sem dados sensíveis.
109
+
110
+ Status HTTP, formato e exemplos: `references/error-handling.md`.
111
+
112
+ ## 6. Rotas (sempre 405)
113
+
114
+ ```dart
115
+ Future<Response> onRequest(RequestContext context) async {
116
+ final controller = context.read<OrderController>();
117
+
118
+ switch (context.request.method) {
119
+ case HttpMethod.post:
120
+ final result = await controller.create(context).run();
121
+ return result.match(
122
+ ErrorResponse.fromClient,
123
+ (order) => Response.json(statusCode: HttpStatus.created, body: order.toJson()),
124
+ );
125
+ case _:
126
+ return Response(statusCode: HttpStatus.methodNotAllowed);
127
+ }
128
+ }
129
+ ```
130
+
131
+ - Rota fina: sem regra de negócio, query, SDK ou instanciação de dependência.
132
+ - `index.dart` para o recurso, `[id].dart` para parâmetro (`onRequest(RequestContext context, String id)`); sem conflito entre `api.dart` e `api/index.dart`.
133
+ - Use `HttpStatus` de `dart:io` em vez de números mágicos.
134
+
135
+ Convenções do roteador, CLI e catálogo de grupos: `references/routing.md`.
136
+
137
+ ## 7. Injeção de dependência por middleware
138
+
139
+ ```dart
140
+ // routes/api/orders/_middleware.dart
141
+ OrderRepositoryPort? _repository;
142
+ OrderService? _service;
143
+ OrderController? _controller;
144
+
145
+ Handler middleware(Handler handler) {
146
+ return handler
147
+ .use(provider<OrderController>((c) => _controller ??= OrderController(c.read<OrderService>())))
148
+ .use(provider<OrderService>((c) => _service ??= OrderService(c.read<OrderRepositoryPort>())))
149
+ .use(provider<OrderRepositoryPort>((c) => _repository ??= OrderRepository(c.read<OrderDataSource>())));
150
+ }
151
+ ```
152
+
153
+ - Construtores recebem **ports/interfaces**; campos `final`.
154
+ - A resolução é de baixo para cima: se `B` depende de `A`, declare `.use(B).use(A)`.
155
+ - `provider` recria o valor a cada leitura; para recursos caros, faça cache em variável privada (`??=`).
156
+ - Infraestrutura compartilhada (conexão, configuração, rate limit) é provida no middleware raiz.
157
+ - Middleware **não** contém regra de negócio nem lê parâmetros de caminho por posição; autorização de recurso fica no service.
158
+ - Nunca `context.read` em models, ports, services ou repositories.
159
+
160
+ Composição por grupo, autenticação e ordem: `references/middlewares-di.md`.
161
+
162
+ ## 8. Configuração, segurança e geração de código
163
+
164
+ - Variáveis de ambiente lidas em um ponto único (`common/environment`); nunca hardcodar segredos nem logar tokens ou dados pessoais. `String.fromEnvironment` não funciona no Dart Frog (use `Platform.environment`).
165
+ - Validação de formato no controller/DTO; de regra de negócio no service; falhas de persistência no repository.
166
+ - Nunca confie em `uid`, papel ou perfil enviados pelo cliente sem validar no servidor.
167
+ - Arquivos gerados (`*.g.dart`) não são editados à mão; regenere com `dart run build_runner build --delete-conflicting-outputs`.
168
+ - `main.dart` só define `init()`/`run()`; não compõe services.
169
+
170
+ ## 9. Testes
171
+
172
+ - Service: fake/mock do port, sem banco real. Controller: entrada válida, inválida, `Right` e `Left` com status correto. Rota: `RequestContext` mockado, cobrindo os métodos suportados **e o 405**. Para criar/executar testes, use o agente `test-writer`.
173
+ - Estrutura em `test/` espelhando `lib/` e `routes/`. Detalhes: `references/testing.md`.
174
+
175
+ ## 10. Checklist de review
176
+
177
+ - [ ] `fpdart` instalado; contratos retornam `TaskEither` com `tryCatch` nas operações que falham?
178
+ - [ ] Rota fina, com `case _` retornando 405?
179
+ - [ ] Dependências compostas só em `_middleware.dart`, nada instanciado no handler?
180
+ - [ ] `BusinessException` / `ClientException` / `MiddlewareException` nas camadas certas, sem vazar causa interna?
181
+ - [ ] `domain/` sem Dart Frog, SDK ou persistência concreta?
182
+ - [ ] Segredos e dados pessoais fora do código e dos logs; testes cobrindo regra nova e o 405?
183
+
184
+ Checklist detalhado: `references/checklist.md`.
185
+
186
+ ## Não fazer
187
+
188
+ - Não instanciar service/repository/controller na rota ou usar service locator.
189
+ - Não retornar `Future` simples, `throw` ou `Exception` genérica nos contratos das camadas.
190
+ - Não colocar regra de negócio em rota, controller ou middleware.
191
+ - Não omitir o 405; não expor stack trace, SQL ou mensagens de SDK.
192
+ - Não referenciar ORM/driver no domínio nem nesta skill.
193
+
194
+ ## 11. Arquivos complementares (leitura sob demanda)
195
+
196
+ Leia só o necessário (`list_skill_files` / `get_skill_file` com `skill="dart-frog"`; ou direto em `dart-frog/` sem as tools).
197
+
198
+ | Dúvida | Arquivo |
199
+ |---|---|
200
+ | Árvore de pastas, variações | `references/project-structure.md` |
201
+ | O que cada camada faz e não faz | `references/layers.md` |
202
+ | `TaskEither`, `tryCatch`, instalação do `fpdart` | `references/fpdart-taskeither.md` |
203
+ | Exceções, status e formato de erro | `references/error-handling.md` |
204
+ | Rotas, 405, CLI, grupos | `references/routing.md` |
205
+ | Middlewares, providers e autenticação | `references/middlewares-di.md` |
206
+ | Testes | `references/testing.md` |
207
+ | Checklist de review | `references/checklist.md` |
208
+ | Exemplos de código (exceções, model, port, service, repository, DTO, controller, middlewares, rotas, testes) | `examples/` |
@@ -0,0 +1,50 @@
1
+ // Caminho: lib/common/middleware/auth_middleware.dart
2
+ // Autentica a requisição e provê o usuário; falhas são MiddlewareException.
3
+ import 'dart:io';
4
+
5
+ import 'package:dart_frog/dart_frog.dart';
6
+
7
+ import '../exception/exceptions.dart';
8
+ import '../http/error_response.dart';
9
+
10
+ /// Usuário autenticado disponibilizado aos handlers.
11
+ class AuthenticatedUser {
12
+ /// Cria o usuário autenticado.
13
+ const AuthenticatedUser({required this.id});
14
+
15
+ /// Identificador do usuário.
16
+ final String id;
17
+ }
18
+
19
+ /// Port de verificação de credenciais (a implementação é um adapter).
20
+ abstract interface class TokenVerifierPort {
21
+ /// Retorna o usuário do [token] ou `null` se inválido.
22
+ Future<AuthenticatedUser?> verify(String token);
23
+ }
24
+
25
+ /// Exige `Authorization: Bearer <token>` e provê [AuthenticatedUser].
26
+ Middleware authMiddleware() {
27
+ return (handler) {
28
+ return (context) async {
29
+ final header = context.request.headers[HttpHeaders.authorizationHeader];
30
+ if (header == null || !header.startsWith('Bearer ')) {
31
+ return ErrorResponse.fromMiddleware(const MiddlewareException(
32
+ statusCode: HttpStatus.unauthorized,
33
+ message: 'Credenciais ausentes.',
34
+ ));
35
+ }
36
+
37
+ final user = await context
38
+ .read<TokenVerifierPort>()
39
+ .verify(header.substring('Bearer '.length));
40
+ if (user == null) {
41
+ return ErrorResponse.fromMiddleware(const MiddlewareException(
42
+ statusCode: HttpStatus.unauthorized,
43
+ message: 'Credenciais inválidas.',
44
+ ));
45
+ }
46
+
47
+ return handler(context.provide<AuthenticatedUser>(() => user));
48
+ };
49
+ };
50
+ }