@thiagodiogo/pscode 1.0.0 → 2.0.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 (87) hide show
  1. package/README.md +2 -2
  2. package/bin/pscode.js +0 -0
  3. package/dist/cli/index.js +6 -7
  4. package/dist/commands/config.d.ts +4 -12
  5. package/dist/commands/config.js +69 -242
  6. package/dist/core/change-metadata/schema.d.ts +1 -0
  7. package/dist/core/change-metadata/schema.js +1 -0
  8. package/dist/core/{archive.d.ts → complete.d.ts} +2 -3
  9. package/dist/core/{archive.js → complete.js} +63 -64
  10. package/dist/core/completions/command-registry.js +5 -9
  11. package/dist/core/config-schema.d.ts +1 -5
  12. package/dist/core/config-schema.js +2 -5
  13. package/dist/core/global-config.d.ts +1 -3
  14. package/dist/core/global-config.js +1 -1
  15. package/dist/core/init.d.ts +2 -0
  16. package/dist/core/init.js +81 -21
  17. package/dist/core/jira-transition.d.ts +16 -0
  18. package/dist/core/jira-transition.js +29 -0
  19. package/dist/core/migration.d.ts +3 -12
  20. package/dist/core/migration.js +10 -72
  21. package/dist/core/presets/dixi.d.ts +32 -0
  22. package/dist/core/presets/dixi.js +405 -0
  23. package/dist/core/profile-sync-drift.js +9 -2
  24. package/dist/core/profiles.d.ts +23 -21
  25. package/dist/core/profiles.js +28 -25
  26. package/dist/core/shared/skill-generation.js +3 -5
  27. package/dist/core/shared/tool-detection.d.ts +2 -2
  28. package/dist/core/shared/tool-detection.js +1 -3
  29. package/dist/core/templates/skill-templates.d.ts +1 -2
  30. package/dist/core/templates/skill-templates.js +1 -2
  31. package/dist/core/templates/workflows/apply-change.js +3 -3
  32. package/dist/core/templates/workflows/archive-change.d.ts +2 -2
  33. package/dist/core/templates/workflows/archive-change.js +10 -10
  34. package/dist/core/templates/workflows/onboard.js +9 -9
  35. package/dist/core/update.d.ts +1 -6
  36. package/dist/core/update.js +5 -29
  37. package/dist/core/workspace/foundation.d.ts +1 -1
  38. package/dist/core/workspace/foundation.js +1 -1
  39. package/dist/core/workspace/legacy-state.js +1 -1
  40. package/dist/core/workspace/skills.d.ts +4 -3
  41. package/dist/core/workspace/skills.js +3 -3
  42. package/package.json +21 -22
  43. package/pscode/content/dixi/architectures/feature-sliced-react/eslint-architecture.mjs.template +44 -0
  44. package/pscode/content/dixi/architectures/feature-sliced-react/features/README.md.template +30 -0
  45. package/pscode/content/dixi/architectures/feature-sliced-react/skeleton.yaml +8 -0
  46. package/pscode/content/dixi/architectures/hexagonal-spring/ArchitectureTest.java.template +41 -0
  47. package/pscode/content/dixi/architectures/hexagonal-spring/skeleton.yaml +11 -0
  48. package/pscode/content/dixi/claude-runtime/CLAUDE.md.java.template +62 -0
  49. package/pscode/content/dixi/claude-runtime/CLAUDE.md.react.template +74 -0
  50. package/pscode/content/dixi/claude-runtime/commands/adr.md +75 -0
  51. package/pscode/content/dixi/claude-runtime/commands/arch-check.md +64 -0
  52. package/pscode/content/dixi/claude-runtime/commands/dod.md +66 -0
  53. package/pscode/content/dixi/claude-runtime/commands/jira-draft.md +80 -0
  54. package/pscode/content/dixi/claude-runtime/commands/jira-setup.md +105 -0
  55. package/pscode/content/dixi/claude-runtime/commands/jira-sync.md +69 -0
  56. package/pscode/content/dixi/claude-runtime/commands/rfc.md +73 -0
  57. package/pscode/content/dixi/claude-runtime/hooks/arch-guard.mjs +101 -0
  58. package/pscode/content/dixi/claude-runtime/hooks/jira-context.mjs +60 -0
  59. package/pscode/content/dixi/claude-runtime/skills/pstld-arch-guardian.md +101 -0
  60. package/pscode/content/dixi/claude-runtime/skills/pstld-commit-crafter.md +98 -0
  61. package/pscode/content/dixi/claude-runtime/skills/pstld-jira-context.md +64 -0
  62. package/pscode/content/dixi/context/java/architecture.md +143 -0
  63. package/pscode/content/dixi/context/java/naming.md +62 -0
  64. package/pscode/content/dixi/context/java/testing.md +162 -0
  65. package/pscode/content/dixi/context/react/architecture.md +119 -0
  66. package/pscode/content/dixi/context/react/naming.md +129 -0
  67. package/pscode/content/dixi/context/react/testing.md +141 -0
  68. package/pscode/content/dixi/context/shared/commits.md +47 -0
  69. package/pscode/content/dixi/context/shared/dev-flow.md +53 -0
  70. package/pscode/content/dixi/context/shared/dod.md +38 -0
  71. package/pscode/content/dixi/context/shared/pr-flow.md +53 -0
  72. package/pscode/content/dixi/kit/java/.editorconfig +25 -0
  73. package/pscode/content/dixi/kit/java/.github/workflows/ci-java.yml +68 -0
  74. package/pscode/content/dixi/kit/java/.husky/commit-msg +2 -0
  75. package/pscode/content/dixi/kit/react/.editorconfig +20 -0
  76. package/pscode/content/dixi/kit/react/.github/workflows/ci-react.yml +80 -0
  77. package/pscode/content/dixi/kit/react/.husky/commit-msg +2 -0
  78. package/pscode/content/dixi/kit/react/.husky/pre-commit +2 -0
  79. package/pscode/content/dixi/kit/react/lint-staged.config.mjs +4 -0
  80. package/pscode/content/dixi/kit/shared/.commitlintrc.yml +15 -0
  81. package/pscode/content/dixi/kit/shared/.github/pull_request_template.md +24 -0
  82. package/schemas/pstld-workflow/schema.yaml +67 -0
  83. package/schemas/pstld-workflow/templates/design.md +15 -0
  84. package/schemas/pstld-workflow/templates/rfc.md +26 -0
  85. package/schemas/pstld-workflow/templates/tasks.md +15 -0
  86. package/dist/core/templates/workflows/sync-specs.d.ts +0 -10
  87. package/dist/core/templates/workflows/sync-specs.js +0 -290
@@ -0,0 +1,73 @@
1
+ # /pstld:rfc — Abertura de RFC estruturado
2
+
3
+ Você é um arquiteto técnico conduzindo um processo de RFC (Request for Comments) estruturado para o projeto atual.
4
+
5
+ ## Passos
6
+
7
+ 1. **Leia o contexto do projeto**
8
+
9
+ - Leia `.pscode-dixi.yaml` na raiz do projeto para determinar `stack` e `family` (java | react | null).
10
+ - Leia `pastelsdd/context/dev-flow.md` para entender o fluxo de desenvolvimento e as etapas do RFC.
11
+ - Se `pastelsdd/context/` não existir, continue com orientações genéricas e informe o usuário que os context docs não estão instalados (sugira `pscode init --profile dixi`).
12
+
13
+ 2. **Colete o escopo da mudança**
14
+
15
+ Pergunte ao usuário (se não fornecido como argumento):
16
+ - Qual é o problema ou oportunidade que motiva a mudança?
17
+ - Qual é o escopo estimado (componente, módulo, serviço, cross-cutting)?
18
+ - Há restrições de prazo ou dependências externas?
19
+
20
+ 3. **Gere o documento RFC**
21
+
22
+ Produza um documento RFC com a seguinte estrutura:
23
+
24
+ ```markdown
25
+ # RFC: <título descritivo>
26
+
27
+ **Status:** Rascunho
28
+ **Autor:** <solicitar ao usuário>
29
+ **Data:** <data atual>
30
+ **Stack:** <detectada via .pscode-dixi.yaml ou "não detectada">
31
+
32
+ ## Problema
33
+ <descrição clara do problema ou oportunidade>
34
+
35
+ ## Contexto
36
+ <situação atual, histórico relevante, dependências>
37
+
38
+ ## Solução Proposta
39
+ <descrição da abordagem escolhida>
40
+
41
+ ## Alternativas Consideradas
42
+ | Alternativa | Prós | Contras |
43
+ |-------------|------|---------|
44
+ | ... | ... | ... |
45
+
46
+ ## Impacto Arquitetural
47
+ <camadas afetadas, contratos modificados, migrações necessárias>
48
+ <adaptar com base na stack: hexagonal (Java) ou feature-sliced (React) conforme dev-flow.md>
49
+
50
+ ## Plano de Implementação
51
+ <fases, dependências, marcos>
52
+
53
+ ## Critérios de Aceitação
54
+ - [ ] ...
55
+
56
+ ## Riscos
57
+ | Risco | Probabilidade | Mitigação |
58
+ |-------|---------------|-----------|
59
+ | ... | ... | ... |
60
+
61
+ ## Referências
62
+ - pastelsdd/context/dev-flow.md
63
+ ```
64
+
65
+ 4. **Adapte por stack**
66
+
67
+ - **Java/Spring:** mencione impacto em camadas hexagonais (domain/application/infrastructure) e ports afetados.
68
+ - **React/Next:** mencione impacto em features, shared e páginas (feature-sliced).
69
+ - **Null/desconhecida:** use seções genéricas sem referência a padrões específicos.
70
+
71
+ 5. **Próximos passos**
72
+
73
+ Ao final, liste as próximas ações: refinamento com o time, criação de change via `pscode propose`, ou implementação direta.
@@ -0,0 +1,101 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ function readStdin() {
5
+ try {
6
+ return readFileSync(0, 'utf-8');
7
+ } catch {
8
+ return '';
9
+ }
10
+ }
11
+
12
+ const cwd = process.cwd();
13
+ const dixiYamlPath = join(cwd, '.pscode-dixi.yaml');
14
+
15
+ // Gate: exit 0 silently when .pscode-dixi.yaml is absent (non-Dixi projects unaffected)
16
+ if (!existsSync(dixiYamlPath)) {
17
+ process.exit(0);
18
+ }
19
+
20
+ let family = null;
21
+ try {
22
+ const yaml = readFileSync(dixiYamlPath, 'utf-8');
23
+ const m = yaml.match(/^family:\s*(\S+)/m);
24
+ if (m) family = m[1].trim();
25
+ } catch {
26
+ process.exit(0);
27
+ }
28
+
29
+ if (family !== 'java' && family !== 'react') {
30
+ process.exit(0);
31
+ }
32
+
33
+ let input = {};
34
+ try {
35
+ const raw = readStdin();
36
+ if (raw.trim()) input = JSON.parse(raw);
37
+ } catch {
38
+ process.exit(0);
39
+ }
40
+
41
+ const toolInput = input.tool_input ?? {};
42
+ const rawFilePath = String(toolInput.file_path ?? '');
43
+ const newContent = String(toolInput.new_string ?? toolInput.content ?? '');
44
+ const filePath = rawFilePath.replace(/\\/g, '/');
45
+
46
+ if (family === 'java') {
47
+ // Check files in src/**/infrastructure/**
48
+ if (/\/infrastructure\//.test(filePath)) {
49
+ const importLines = newContent.split('\n').filter(l => /^\s*import\s+/.test(l));
50
+ // Violation: import from domain.* but NOT domain.port.*
51
+ const hasDirectDomainImport = importLines.some(
52
+ l => /\.domain\./.test(l) && !/\.domain\.port/.test(l)
53
+ );
54
+ if (hasDirectDomainImport) {
55
+ process.stdout.write(
56
+ `Violação hexagonal: ${rawFilePath} importa diretamente de domain sem porta. Consulte pastelsdd/context/architecture.md\n`
57
+ );
58
+ process.exit(2);
59
+ }
60
+ }
61
+ process.exit(0);
62
+ }
63
+
64
+ if (family === 'react') {
65
+ // Cross-feature import check for files in src/features/**
66
+ const featureMatch = filePath.match(/\/features\/([^/]+)/);
67
+ if (featureMatch) {
68
+ const currentFeature = featureMatch[1];
69
+ const importRegex = /from\s+['"]([^'"]+)['"]/g;
70
+ let m;
71
+ while ((m = importRegex.exec(newContent)) !== null) {
72
+ const importPath = m[1];
73
+ const featureInImport = importPath.match(/features\/([^/']+)/);
74
+ if (featureInImport && featureInImport[1] !== currentFeature) {
75
+ process.stdout.write(
76
+ `Violação feature-sliced: importação cruzada entre features. Consulte pastelsdd/context/architecture.md\n`
77
+ );
78
+ process.exit(2);
79
+ }
80
+ }
81
+ }
82
+
83
+ // Warning: combined useState+useEffect with large body in src/app/** or src/pages/**
84
+ if (/\/(app|pages)\//.test(filePath)) {
85
+ const lines = newContent.split('\n');
86
+ if (
87
+ lines.length > 10 &&
88
+ /useState/.test(newContent) &&
89
+ /useEffect/.test(newContent)
90
+ ) {
91
+ process.stdout.write(
92
+ `Aviso: lógica inline detectada em page/app. Considere extrair para um hook ou service.\n`
93
+ );
94
+ process.exit(1);
95
+ }
96
+ }
97
+
98
+ process.exit(0);
99
+ }
100
+
101
+ process.exit(0);
@@ -0,0 +1,60 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ function readStdin() {
5
+ try {
6
+ return readFileSync(0, 'utf-8');
7
+ } catch {
8
+ return '';
9
+ }
10
+ }
11
+
12
+ const cwd = process.cwd();
13
+
14
+ let input = {};
15
+ try {
16
+ const raw = readStdin();
17
+ if (raw.trim()) input = JSON.parse(raw);
18
+ } catch {
19
+ process.exit(0);
20
+ }
21
+
22
+ const prompt = String(input.prompt ?? '');
23
+ const ticketMatch = prompt.match(/[A-Z]+-\d+/);
24
+ if (!ticketMatch) {
25
+ process.exit(0);
26
+ }
27
+
28
+ const jiraYamlPath = join(cwd, 'pastelsdd', 'jira.yaml');
29
+ if (!existsSync(jiraYamlPath)) {
30
+ process.exit(0);
31
+ }
32
+
33
+ let jiraContent = '';
34
+ try {
35
+ jiraContent = readFileSync(jiraYamlPath, 'utf-8');
36
+ } catch {
37
+ process.exit(0);
38
+ }
39
+
40
+ const projectKeyMatch = jiraContent.match(/^project_key:\s*(\S+)/m);
41
+ if (!projectKeyMatch) {
42
+ process.exit(0);
43
+ }
44
+
45
+ const ticket = ticketMatch[0];
46
+ const projectKey = projectKeyMatch[1];
47
+ const boardUrlMatch = jiraContent.match(/^board_url:\s*(.+)/m);
48
+ const boardUrl = boardUrlMatch ? boardUrlMatch[1].trim() : null;
49
+
50
+ const lines = [
51
+ `[Contexto JIRA]`,
52
+ `Ticket detectado: ${ticket}`,
53
+ `Projeto: ${projectKey}`,
54
+ ];
55
+ if (boardUrl) {
56
+ lines.push(`Board: ${boardUrl}`);
57
+ }
58
+
59
+ process.stdout.write(lines.join('\n') + '\n');
60
+ process.exit(0);
@@ -0,0 +1,101 @@
1
+ # pstld-arch-guardian
2
+
3
+ ## Trigger
4
+
5
+ Esta skill é auto-invocada **antes de aplicar uma edição** em arquivos que se enquadrem em:
6
+
7
+ - Java: `src/**/infrastructure/**`
8
+ - React/Next: `src/features/**`, `src/app/**`, `src/pages/**`
9
+
10
+ ---
11
+
12
+ ## Comportamento
13
+
14
+ ### 1. Detectar stack do projeto
15
+
16
+ Leia `.pscode-dixi.yaml` na raiz do projeto e extraia o campo `family`.
17
+
18
+ - Se o arquivo **não existir** ou `family` for `null` ou não reconhecido → **não faça nenhuma verificação**, prossiga normalmente.
19
+ - Se `family: java` → aplique verificação hexagonal (seção 2).
20
+ - Se `family: react` → aplique verificação feature-sliced (seção 3).
21
+
22
+ ---
23
+
24
+ ### 2. Verificação hexagonal (Java)
25
+
26
+ Aplicável quando `family: java` e o arquivo editado está em `src/**/infrastructure/**`.
27
+
28
+ **Bloqueio — import direto de domain sem porta:**
29
+
30
+ Verifique se o novo conteúdo do arquivo contém imports de pacotes como:
31
+ - `import ...domain.model.*`
32
+ - `import ...domain.entity.*`
33
+ - `import ...domain.service.*`
34
+
35
+ ...sem passar por uma interface de porta (`domain.port.in.*` ou `domain.port.out.*`).
36
+
37
+ Se detectar esta violação:
38
+
39
+ > ❌ **Violação hexagonal detectada**
40
+ >
41
+ > O arquivo `[caminho do arquivo]` importa diretamente de `domain/` sem usar uma porta.
42
+ > A regra de dependência exige que `infrastructure` acesse `domain` apenas via interfaces de porta.
43
+ >
44
+ > Consulte `pastelsdd/context/architecture.md` para a lista de imports permitidos.
45
+ >
46
+ > **A edição foi bloqueada.** Refatore para usar a porta adequada antes de continuar.
47
+
48
+ **Permitido — import via porta:**
49
+
50
+ Se o arquivo importa apenas de `domain.port.in.*` ou `domain.port.out.*`, permita a edição sem mensagem.
51
+
52
+ **Fora de infrastructure:**
53
+
54
+ Se o arquivo não está em `src/**/infrastructure/**`, não aplique nenhuma verificação hexagonal.
55
+
56
+ ---
57
+
58
+ ### 3. Verificação feature-sliced (React/Next)
59
+
60
+ Aplicável quando `family: react`.
61
+
62
+ #### 3a. Import cruzado entre features (bloqueio)
63
+
64
+ Se o arquivo editado está em `src/features/<feature-a>/` e o novo conteúdo importa de outro diretório sob `src/features/` (ex: `../feature-b/`, `../../features/feature-b/`):
65
+
66
+ > ❌ **Violação feature-sliced detectada**
67
+ >
68
+ > O arquivo `[caminho]` importa de outra feature (`[feature-b]`).
69
+ > Features devem ser independentes entre si — imports cruzados criam acoplamento proibido.
70
+ >
71
+ > Use `src/shared/` para código reutilizado entre features.
72
+ > Consulte `pastelsdd/context/architecture.md`.
73
+ >
74
+ > **A edição foi bloqueada.**
75
+
76
+ **Permitido:** imports dentro da mesma feature ou de `src/shared/`.
77
+
78
+ #### 3b. Lógica de negócio inline em páginas (warning)
79
+
80
+ Se o arquivo editado está em `src/app/**` ou `src/pages/**` e o novo conteúdo contém qualquer um dos sinais abaixo:
81
+ - Hook `useState` + `useEffect` com corpo de mais de 10 linhas no mesmo componente
82
+ - Função de transformação/validação de dados definida inline no componente (não em hook separado)
83
+
84
+ Emita aviso (não bloqueie):
85
+
86
+ > ⚠️ **Atenção — lógica de negócio em página detectada**
87
+ >
88
+ > O componente em `[caminho]` parece conter lógica de negócio inline.
89
+ > Considere extrair para um custom hook em `src/features/` ou `src/shared/hooks/`.
90
+ >
91
+ > Consulte `pastelsdd/context/architecture.md` para o padrão recomendado.
92
+ >
93
+ > A edição foi aplicada, mas revise antes de finalizar.
94
+
95
+ ---
96
+
97
+ ## Comportamento seguro
98
+
99
+ - Se `.pscode-dixi.yaml` não existe → nenhuma ação, nenhum erro.
100
+ - Se `family` tem valor desconhecido → nenhuma ação.
101
+ - Em nenhuma hipótese esta skill modifica arquivos fora do arquivo-alvo da edição original.
@@ -0,0 +1,98 @@
1
+ # pstld-commit-crafter
2
+
3
+ ## Trigger
4
+
5
+ Esta skill é auto-invocada quando o usuário mencionar **commit** no prompt ou solicitar fazer commit:
6
+
7
+ - "commit", "fazer commit", "commitar", "cria um commit", "faz o commit", "git commit"
8
+
9
+ Ative **antes** de executar qualquer `git commit`.
10
+
11
+ ---
12
+
13
+ ## Comportamento
14
+
15
+ ### 1. Detectar stack para inferência de escopo
16
+
17
+ Leia `.pscode-dixi.yaml` na raiz do projeto e extraia `family`.
18
+
19
+ **Se `family: java`:**
20
+ - Inspecione os arquivos modificados via `git status` / `git diff --name-only`.
21
+ - Identifique o bounded context pelo diretório após o base package.
22
+ - Ex: `src/main/java/com/empresa/payment/` → escopo `payment`
23
+ - Ex: `src/main/java/com/empresa/order/domain/` → escopo `order`
24
+
25
+ **Se `family: react`:**
26
+ - Identifique o nome da feature pelo diretório em `src/features/`.
27
+ - Ex: `src/features/user-management/` → escopo `user-management`
28
+ - Ex: `src/features/checkout/` → escopo `checkout`
29
+
30
+ **Se `.pscode-dixi.yaml` não existir ou `family` for `null`:**
31
+ - Infira o escopo pelo diretório principal dos arquivos modificados.
32
+ - Ex: maioria em `src/services/billing/` → escopo `billing`
33
+ - Ex: maioria em `src/` → escopo do módulo mais frequente nos caminhos
34
+
35
+ ---
36
+
37
+ ### 2. Verificar ticket JIRA
38
+
39
+ Leia `pastelsdd/jira.yaml` na raiz do projeto.
40
+
41
+ - Se o arquivo **não existir** ou tiver `configured: false`: mensagem sem ticket JIRA (vá para seção 3).
42
+ - Se tiver `project_key` configurado: o ticket **é obrigatório**.
43
+
44
+ **Ticket presente no prompt:** use diretamente (ex: usuário mencionou "PROJ-42").
45
+
46
+ **Ticket ausente no prompt:** pergunte ao usuário:
47
+
48
+ > Qual o número do ticket JIRA? (ex: 42)
49
+ > O project key configurado é `[PROJECT_KEY]`.
50
+
51
+ Aguarde a resposta antes de continuar. Monte o sufixo `[PROJECT_KEY-NNN]` com o número fornecido.
52
+
53
+ ---
54
+
55
+ ### 3. Montar a mensagem de commit
56
+
57
+ Identifique o tipo baseado nos arquivos modificados e no contexto:
58
+
59
+ | Tipo | Quando usar |
60
+ |------|-------------|
61
+ | `feat` | nova funcionalidade |
62
+ | `fix` | correção de bug |
63
+ | `refactor` | mudança sem alterar comportamento |
64
+ | `test` | adição/correção de testes |
65
+ | `docs` | documentação apenas |
66
+ | `chore` | configuração, build, dependências |
67
+
68
+ Gere a mensagem no formato:
69
+
70
+ ```
71
+ tipo(escopo): descrição concisa em português ou inglês [PROJECT_KEY-NNN]
72
+ ```
73
+
74
+ Sem ticket quando `pastelsdd/jira.yaml` não configurado:
75
+
76
+ ```
77
+ tipo(escopo): descrição concisa
78
+ ```
79
+
80
+ **Exemplos:**
81
+ - `feat(payment): adiciona validação de cartão de crédito [PROJ-99]`
82
+ - `fix(user-management): corrige redirect após logout [APP-12]`
83
+ - `refactor(order): extrai lógica de cálculo para OrderPricingService [PROJ-55]`
84
+ - `test(checkout): cobre cenário de pedido sem estoque [SHOP-7]`
85
+
86
+ ---
87
+
88
+ ### 4. Apresentar a mensagem ao usuário
89
+
90
+ Exiba a mensagem proposta e pergunte:
91
+
92
+ > Mensagem de commit proposta:
93
+ > ```
94
+ > tipo(escopo): descrição [PROJECT_KEY-NNN]
95
+ > ```
96
+ > Deseja usar esta mensagem ou ajustar?
97
+
98
+ Se o usuário aprovar, execute o commit. Se quiser ajustar, aplique a correção e confirme novamente.
@@ -0,0 +1,64 @@
1
+ # pstld-jira-context
2
+
3
+ ## Trigger
4
+
5
+ Esta skill é auto-invocada quando o prompt do usuário contém uma string no formato `[A-Z]+-\d+`:
6
+
7
+ - Exemplos: `PROJ-123`, `MYAPP-42`, `BE-7`, `PAYMENT-1001`
8
+ - A ativação ocorre **antes de gerar a resposta**, para enriquecer o contexto.
9
+
10
+ Se o prompt não contém nenhum padrão `[A-Z]+-\d+`, a skill não é ativada.
11
+
12
+ ---
13
+
14
+ ## Comportamento
15
+
16
+ ### 1. Verificar configuração JIRA
17
+
18
+ Leia `pastelsdd/jira.yaml` na raiz do projeto.
19
+
20
+ **Se o arquivo não existir ou tiver `configured: false`:**
21
+
22
+ Exiba o seguinte aviso **uma única vez por sessão** (não repita em prompts subsequentes):
23
+
24
+ > ℹ️ Integração JIRA não configurada — edite `pastelsdd/jira.yaml` para habilitar.
25
+
26
+ Prossiga sem buscar contexto do ticket.
27
+
28
+ **Se tiver `configured: true`:** continue para a seção 2.
29
+
30
+ ---
31
+
32
+ ### 2. Buscar contexto do ticket via MCP
33
+
34
+ Use a ferramenta `mcp__atlassian__getJiraIssue` com a chave detectada no prompt (ex: `PROJ-123`).
35
+
36
+ **Se o ticket for encontrado**, injete as seguintes informações como contexto adicional **antes** de gerar a resposta:
37
+
38
+ > **Contexto JIRA — [CHAVE]**
39
+ >
40
+ > **Título:** [título do ticket]
41
+ >
42
+ > **Descrição:**
43
+ > [descrição do ticket]
44
+ >
45
+ > **Critérios de aceite:**
46
+ > [critérios de aceite, se disponíveis]
47
+
48
+ Em seguida, prossiga normalmente para responder ao prompt do usuário.
49
+
50
+ ---
51
+
52
+ ### 3. Tratamento de erro
53
+
54
+ **Se `mcp__atlassian__getJiraIssue` retornar erro** (ticket inexistente, permissão negada, MCP indisponível):
55
+
56
+ > ⚠️ Não foi possível obter contexto de `[CHAVE]`: [mensagem de erro resumida]. Prosseguindo sem contexto JIRA.
57
+
58
+ Não bloqueie a resposta — prossiga normalmente.
59
+
60
+ ---
61
+
62
+ ## Controle de sessão
63
+
64
+ O aviso "Integração JIRA não configurada" deve ser exibido **no máximo uma vez por sessão**. Se já foi exibido, omita-o em ativações subsequentes e prossiga em silêncio.
@@ -0,0 +1,143 @@
1
+ # Arquitetura — Java/Spring (Hexagonal)
2
+
3
+ ## Estrutura de pacotes obrigatória
4
+
5
+ ```
6
+ com.example.app/
7
+ ├── domain/
8
+ │ ├── model/ # Entidades, value objects, agregados
9
+ │ ├── port/
10
+ │ │ ├── in/ # Use case interfaces (portas de entrada)
11
+ │ │ └── out/ # Repository, gateway interfaces (portas de saída)
12
+ │ └── exception/ # Exceções de domínio
13
+ ├── application/
14
+ │ └── usecase/ # Implementações dos use cases
15
+ └── infrastructure/
16
+ └── adapter/
17
+ ├── in/ # Controllers REST, consumers de mensagem
18
+ └── out/ # Implementations de repository, clientes HTTP
19
+ ```
20
+
21
+ ## Regras de dependência
22
+
23
+ ```
24
+ infrastructure → application → domain
25
+ ```
26
+
27
+ - `domain` não importa nada externo ao próprio domínio
28
+ - `application` importa apenas `domain`
29
+ - `infrastructure` importa `application` e `domain`, mas nunca ao contrário
30
+ - Frameworks Spring (anotações, contexto) pertencem à camada `infrastructure`
31
+
32
+ ## Exemplos por camada
33
+
34
+ ### Domain — modelo
35
+
36
+ ```java
37
+ // domain/model/Pedido.java
38
+ public class Pedido {
39
+ private final PedidoId id;
40
+ private final List<ItemPedido> itens;
41
+ private StatusPedido status;
42
+
43
+ public void confirmar() {
44
+ if (itens.isEmpty()) throw new PedidoVazioException();
45
+ this.status = StatusPedido.CONFIRMADO;
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Domain — porta de entrada
51
+
52
+ ```java
53
+ // domain/port/in/ConfirmarPedidoUseCase.java
54
+ public interface ConfirmarPedidoUseCase {
55
+ Pedido confirmar(PedidoId pedidoId);
56
+ }
57
+ ```
58
+
59
+ ### Domain — porta de saída
60
+
61
+ ```java
62
+ // domain/port/out/PedidoRepository.java
63
+ public interface PedidoRepository {
64
+ Optional<Pedido> findById(PedidoId id);
65
+ Pedido save(Pedido pedido);
66
+ }
67
+ ```
68
+
69
+ ### Application — use case
70
+
71
+ ```java
72
+ // application/usecase/ConfirmarPedidoService.java
73
+ @Service
74
+ public class ConfirmarPedidoService implements ConfirmarPedidoUseCase {
75
+ private final PedidoRepository repository;
76
+
77
+ @Override
78
+ public Pedido confirmar(PedidoId pedidoId) {
79
+ Pedido pedido = repository.findById(pedidoId)
80
+ .orElseThrow(() -> new PedidoNaoEncontradoException(pedidoId));
81
+ pedido.confirmar();
82
+ return repository.save(pedido);
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Infrastructure — adapter de entrada
88
+
89
+ ```java
90
+ // infrastructure/adapter/in/PedidoController.java
91
+ @RestController
92
+ @RequestMapping("/pedidos")
93
+ public class PedidoController {
94
+ private final ConfirmarPedidoUseCase confirmarPedido;
95
+
96
+ @PostMapping("/{id}/confirmar")
97
+ public ResponseEntity<PedidoResponse> confirmar(@PathVariable String id) {
98
+ Pedido pedido = confirmarPedido.confirmar(new PedidoId(id));
99
+ return ResponseEntity.ok(PedidoMapper.toResponse(pedido));
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Infrastructure — adapter de saída
105
+
106
+ ```java
107
+ // infrastructure/adapter/out/PedidoJpaRepository.java
108
+ @Repository
109
+ public class PedidoJpaRepository implements PedidoRepository {
110
+ private final PedidoJpaEntityRepository jpa;
111
+
112
+ @Override
113
+ public Optional<Pedido> findById(PedidoId id) {
114
+ return jpa.findById(id.value()).map(PedidoMapper::toDomain);
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## Regras adicionais
120
+
121
+ - **Sem lógica de negócio em controllers ou repositories** — toda regra de negócio fica em `domain` ou `application`
122
+ - **Entidades de domínio são imutáveis por padrão** — use construtores e métodos explícitos, evite setters
123
+ - **Mappers** ficam em `infrastructure` para converter entre entidades de domínio e entidades JPA/DTOs
124
+ - **Anotações Spring** (`@Service`, `@Repository`, `@RestController`) pertencem à infraestrutura, não ao domínio
125
+
126
+ ## Skeleton e guardrails automáticos (profile dixi)
127
+
128
+ `pscode init --profile dixi` cria automaticamente o skeleton de pastas hexagonal com `.gitkeep` em cada diretório e gera `src/test/java/{basePackage}/ArchitectureTest.java` com três regras ArchUnit pré-configuradas:
129
+
130
+ 1. `domain` não depende de `infrastructure`
131
+ 2. `application` não depende de `infrastructure`
132
+ 3. `infrastructure.adapter.in` não depende de `infrastructure.adapter.out`
133
+
134
+ O `basePackage` é detectado automaticamente do `pom.xml` (`groupId` + `artifactId` sem hífens). Adicione a dependência ArchUnit ao `pom.xml` para ativar as verificações:
135
+
136
+ ```xml
137
+ <dependency>
138
+ <groupId>com.tngtech.archunit</groupId>
139
+ <artifactId>archunit-junit5</artifactId>
140
+ <version>1.3.0</version>
141
+ <scope>test</scope>
142
+ </dependency>
143
+ ```